How to break your Android App with proguard / R8

Paul Woitaschek
YAZIO Engineering
Published in
2 min readJun 8, 2020

I recently updated the android gradle plugin to 4.0.0. While developing, everything went smoothly and at some point I was ready to cut a release.
The very last manual testing of the release app bundle was also fine so no Proguard / R8 issues on the first sight either.

Then I thought: Let me check the CI results again. Everything was fine, but: The instrumentation tests. Almost all of them were timing out. After inspecting the logs, they all hang at the end of the on-boarding. Strangely the loading spinner was loading for way longer than the timeout should be.
So I tested it on my phone and faced the same results.

At that time I had no idea it was caused by updating AGP, so I did a 2 hours long git bisect.

Okay, now lets create a minimal self reproducible bug report because I found a huge bug in AGP, everyone should know about immediately.

I created a Hello World project that was making a single network call and printed it results into a TextView. Strangely here it did not reproduce.
After experimenting with the OkHttp configuration and testing if it was related to some gradle plugin, I finally had the idea that it might be related to R8.

So I added my proguard configuration and now the network call hang!
Let’s locate the issue further. I was step by step removing lines from my proguard configuration until only a single line was left:

-assumenosideeffects class android.util.Log { public * ; }

Now what is this? How is that even slightly related to anything? It should remove the log spam of some third party libraries we’re using.

So I was asking Mads Ager. (he works on D8/R8)

you have to be very careful with -assumenosideeffects The problem is that in order for -assumenosideeffects to have the effect of actually removing calls, it matches up the class hierarchy. Therefore, this rule says that anything public in android.util.Log and its superclasses has no side-effects. That include the synchronization methods defined on Object. So, please don't use * wildcards in connection with -assumenosideeffects. :(

That means, that this tiny snippet you can find all over the place on the internet, breaks the fundamental synchronization of your application and can bring you into really weird states. Because the single one super class of android.util.Log is java.lang.Object . And Object#notify is very much with side effects.

So be really careful with assumenosideeffectsand always think about specifying exact signatures because it affects the super classes as well.
Oh, and if some library developer puts this into it’s consumer proguard rules you have a real issue.

If you need a replacement for removing log calls, specify all signatures:

# no logging in production
-assumenosideeffects class android.util.Log {
v(...);
d(...);
i(...);
w(...);
e(...);
println(...);
}

And feel free to star the ticket on the lint rule: https://issuetracker.google.com/issues/157893915

--

--