Proguard: avoiding naming collisions with pre-obfuscated library JARs - java

It seems that Proguard doesn't make any attempt to avoid naming collisions with classes in library JARs when it renames/repackages classes. Is this correct, or have I just not configured it correctly?
I am obfuscating an Android application that uses the latest Google AdMob SDK. Previously I was using the old AdMob SDK without a problem. The new SDK JAR file contains some classes that have been obfuscated. One of these classes is a.class in the default/unnamed package. When I obfuscate my app, Proguard renames/repackages one of my classes to also be a.class in the unnamed package, despite having read in the AdMob JAR as a library JAR (so it ought to know that this will cause a collision). Predictably, my build fails when the dx tool attempts to combine these two identically named classes in a single .dex file.
As a workaround I have reconfigured Proguard so that it moves all of my classes into a named package (just a single letter) to avoid collisions with the Google classes, but I'm interested to know if there is a better solution or if this is a limitation of the current version (4.6) of Proguard?

From the progaurd manual,
If an input jar and a library jar contain classes in the same package, the obfuscated output jar may contain class names that overlap with class names in the library jar. This is most likely if the library jar has been obfuscated before, as it will then probably contain classes named 'a', 'b', etc. Packages should therefore never be split across input jars and library jars.
So it looks like using your own package is the recommended answer.

In closed libraries to prevent conflict between multiple obfuscated modules/libraries you should use
-keeppackagenames
proguard rule to prevent complete classes repackaging, otherwise you can find errors like:
Duplicate class a.a.a.a in [jetified-lib1] and a.a.a.a in [jetified-lib2]

Related

Include library for JAXB into JAR file without using Maven

I have a Java project in Eclipse that uses javax.xml.bind.JAXB classes.
Starting the application from inside Eclipse works perfectly.
However, when I export the project as (runnable) jar file and run it using java -jar myfile.jar it terminates with a java.lang.NoClassDefFoundError: javax/xml/bin/JAXBException.
Also playing around with the three options for Library handling in Eclipse Runnable JAR File Specification (extract, package, sub-folder) does not solve the problem - in fact, no libraries are exported in any case.
It seems that the library for JAXB (it seems to be rt.jar) is not considered as required library to be included into the jar file. However, when running the jar file, it is not found nevertheless.
I have read that the library must be added to the classpath but this seems strange to me as rt.jar is part of the standard libraries. Is there something special about this library?
Currently, I do not use Maven or something similar for dependency and build management and if possible I want to avoid it for the future. I think, there also must be a way without Maven.
I found several posts here on SO and in Google but was not able to work out a solution for me.
Thank you very much!
As remarked in the comments, Eclipse probably uses a different Java version than your system (by default). The JAXB API and implementation is not available in JRE 11.
To work on all versions of Java, your best option is:
Download the JAXB RI distribution. Nowadays I'll choose version 3.0 (which is binary incompatible with the one in Java 8, since it uses jakarta.xml instead of javax.xml for the packages name) as in Juliano's answer:
https://repo1.maven.org/maven2/com/sun/xml/bind/jaxb-ri/3.0.0/jaxb-ri-3.0.0.zip
Copy the 4 files jakarta.activation.jar, jakarta.xml.bind-api.jar, jaxb-core.jar and jaxb-impl.jar from the mod folder into the library folder of your project (let's say lib),
Add the 4 libraries to the project's "Build Path",
Make sure you use JAXB 3.0 throughout your code (the packages of the annotations and classes start with jakarta.xml)
Run the application once in Eclipse, so it updates the Run Configuration (or update the classpath of the Run Configuration yourself),
Export the project to a JAR file.
Among the three export options proposed by Eclipse: "extract required libraries" will create a so-called fat jar (everything in one JAR-file). It works, but it deletes the licence notices in the JAXB jars (so it can not be distributed). "copy required libraries" is your best option, but then you have to move the jar file together with the subfolder. _". "package required libraries" will not work, since jars in a jar are not read by the JVM (unlike JARs in a WAR package).
Edit by the author of the question:
The above worked for me well except that I experienced small differences how the two libraries (javax.xml in Java 8 and jakarta.xml in version 3.0) handle #XmlAttribute annotations. In javax.xml, I could place an annotation without further arguments on the public getter-method, e.g.
#XmlAttribute
public String getDescription() {
return "";
}
And this worked when the attribute name in the xml file is description. However, with jakarta.xml I had to add the name of the attribute:
#XmlAttribute(name="description")
public String getDescription() {
return "";
}
Just in the case, that others experience the same problem.
I thought about this myself too, since I am new to java.
There is a description of a Extension Mechanism in the java tutorials (SE), but it is no longer used since deprecated by Oracle. See, just to know of what I am talking about: https://docs.oracle.com/javase/tutorial/ext/index.html
What was this Extension thing in a nutshell: just drop your jar files inside the jdk lib and you could use the import keyword in all your classes to use the new jar file.
However, others had to do the same thing in their computers to run a class which imported your own update to the jdk.
Maven do something like the above. It searches on the pom file which other jar files it should include in your jar when you build an application. Hence, it may run anywhere.
Another way of looking into this is the answer which you should try to do.
A clunckier way of doing what Maven does without its pom structure is to create a new folder inside your src folder and copy the jakarta.xml.bind-api.jar. Just like when you create an object (aJavaBean) and need to use it in another class.
The file you need to include in your library is available at:
https://repo1.maven.org/maven2/com/sun/xml/bind/jaxb-ri/3.0.0/jaxb-ri-3.0.0.zip
Finally, extract the classes inside this newly created folder and use the import keyword in the classes that depend on it just like when you create your own classes.
Another thing you should try is to use the manifest file when making your jar.
https://docs.oracle.com/javase/tutorial/deployment/jar/manifestindex.html
This tutorial shows how to include a classpath to the files you need to run as a dependency. Make sure that everything you need is inside the newly created jar file.
Also, set the entry point in the manifest, so your application can run just using
java -jar MyJar.jar
in the command line.
The easiest way is to use JDK 8 (or older JDK) that has embedded the required jaxb library. The hard way requires that you set your CLASSPATH variable pointing to each required jaxb jar file.
From spec at https://javaee.github.io/jaxb-v2/doc/user-guide/release-documentation.html#a-2-3-0, the following jars are required using a java version 11 or above.
jaxb-api.jar
jaxb-core.jar
jaxb-impl.jar
A good article on this question is https://www.jesperdj.com/2018/09/30/jaxb-on-java-9-10-11-and-beyond/

How to Import a prebuilt *.so library and call it from the new project?

I'm currently working on a project which will use a prebuilt shared library. But I'm quite confused on the process of importing a prebuilt library, as well as calling the methods in the prebuilt from the new project java class.
Let's say:
Project A has generated the libA.so files for each ABI_ARCH.
I'm trying to use them in my Project B.
What is the proper way to import them, and how to call the methods of libA.so which are declared in A. In another word how to use the exposed APIs of libA.so ?
These days Google has pre-released a new feature that will allow easy reuse of prebuilt native libraries. If you are not ready yet to jump to Android Studio 4.0, and/or to build the necessary AAR for libA, you can use the old ways.
There are three different tracks, depending on what the nature of your Project B is.
If it only loads libA.so from Java/Kotlin, it's enough to specify jniLibs.src in build.gradle: How can I add .so files to an android library project using gradle 0.7+. Note that all classes that have native methods which are implemented in libA.so must be copied to Project B keeping their fully qualified names intact.
If the second project uses ndk-build, you should include $(PREBUILT_SHARED_LIBRARY). An NDK guide has further up-to-date details.
If the project uses CMake, you use add_library(… SHARED IMPORTED). An Android Srudio documentation explains different scenarios.
It's important to emphasise a tiny difference between ndk-build and CMake in their treatment of prebuilt .so libraries. While ndk-build will copy these libraries to the installation directory with all libraries that it builds, CMake does not provide this assistance. Therefore, you will probably add the folder that holds the prebuilt library to jniLibs.src in build.ghradle (as in the case 1 above).
In all 3 scenarios, make sure that the libraries that you get packed into your APK or AAB are stripped of debug symbols.

Minimizing external JAR by removing all non-dependent classes from source code

I have an Android project which requires Apache's POI external JARs (libraries used to read and manipulate MS Office files, but that's irrelevant).
Upon compilation I get an error stating that my project has over 65536 method declarations - I only have a few dozens and the rest are from the external libraries.
I only need a fraction of the functionality of POI so I downloaded the full source code and imported it into Eclipse. I have a list of classes I'd like to keep, however each one of these classes depends on other classes in the code, and I can only assume that these classes also have dependencies.
Is there a way to clean the source code so I'd keep only the classes I need and all their dependencies (without manually tracking each dependency, as there are a lot of them)?
P.S - I've looked at this question but it deals with compiled JARs and I also have the source code.

Create sources jar for shrunk classes in a module via proguard

There is a business module - say kilo-business in one of the applications. Now there are some APIs in this module that is to be used by another application (locally within the organization - say kilo-client1). I have used Proguard (via its maven plugin) to only extract the classes relevant to the API and make a JAR out of it - the shrunk API JAR called kilo-business-apis. kilo-client1 uses this kilo-business-apis dependency and invokes the service. As a policy, we always include sources along with any artifact that we share between applications (for debugging ease and additional documentation).
In this case, is there a way that one can have Proguard also give out the (probable) source files (belonging to kilo-business module) from where it has sourced the classes for the kilo-business-apis using which a sources JAR can be conjured? Actually, we don't shrink the JAR to the extent of removing unused methods/variables from the class, so giving out the source java files would also suffice.
I understand that one can use jd-eclipse or some other decompiler in eclipse to aid debugging even if we don't publish it, but wanted to check if we can generate the sources first class whereby documentation needs are also addressed.
One thought is to break up kilo-business itself into a separate module for kilo-business-apis where only the API definitions reside and both kilo-business and kilo-client1 can use it, but unfortunately I can't do it for legacy reasons.
Thanks in advance!
ProGuard optionally writes out information about kept entry points (-printseeds), unused code (-printusage), and obfuscation mapping (-printmapping), but nothing related to source files. If you don't obfuscate the code, you can simply list the .class names in the processed output, and find the corresponding .java files.

Obfuscating an Android Library project / apklib

I have a question regarding how to obfuscate an Android library project. Overall, I'd like to obfuscate an apklib file (or really the .java files for any library project) that contains both .java files (in the Android src/ directory) and XML files / external resources (in the Android res/ directory). I'd like to be able to distribute this library to other developers, similarly to the Facebook SDK library (https://github.com/facebook/facebook-android-sdk for reference). However, I want the code to be obfuscated so that my methods aren't exposed in an easy to read manner.
I've tried using Proguard with Maven, but even after building my project with Proguard the files in my .apklib were not obfuscated. I also tried running Proguard manually, but I wasn't able to have it output raw .java files (that could be referenced by another project as a library) instead of .class files.
If anybody has any information on how to obfuscate the java code in an apklib / Android library, I'd be very grateful. Additionally, it would also be very helpful to know if it isn't possible to create an obfuscated andoroid library at all.
Thanks very much for your help.
Best,
Kevin
Depending on how you set up the proguard goal of the android maven plugin you can most likely create an apklib that is obfuscated. I have however not tried myself. Keep in mind that proguard obfuscates into a secondary artifact potentially and not into the primary apklib.
You should also keep in mind that if you obfuscate the library your users will have to use the obfuscated method names... which sort of defeats the purposed of the distribution and will make it very hard to use so it might be a bad idea. The only thing this would work is if you obfuscate all the code without obfuscating any public method and class names..

Categories

Resources