Using ProGuard WITHOUT Android Studio - android

I would like to know how to run ProGuard from the command line.
I've built a hybrid app using Cordova with the Ionic Framework. One of the recommendations made to me was using ProGuard to obfuscate the java code and to remove the Log calls.
I'm not currently using Android Studio or Eclipse, I just have the project build out in Ionic Framework and Angular 1.X with the Cordova plugins I need for functionality on devices. I have Jenkins build my releases via command line.
I tried to follow the steps laid out here:
https://developer.android.com/studio/build/shrink-code.html#keep-code
and here:
Disable LogCat Output COMPLETELY in release Android app?
As well as combing through various questions/answers to find applicable bits.
My build.gradle is kept in ./vendor directory and copied to overwrite the autogenerated/default build.gradle from Ionic.
It has this for enabling ProGuard:
buildTypes {
debug {
debuggable true
minifyEnabled false
}
release {
signingConfig signingConfigs.release
debuggable false
minifyEnabled true
runProguard true
proguardFile 'customProGuard.txt'
proguardFiles getDefaultProguardFile('proguard-android.txt'),
'proguard-android.txt'
}
}
I've also tried various versions of this line in my project.properties file:
proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt or proguard.config=${sdk.dir}/tools/proguard/proguard-android-optimize.txt:proguard-project.txt
It doesn't appear to be doing anything. I thought my apk file size changed at one point, but when I look at the classes in JD-GUI I can still clearly discern things. So I don't think it's running. Any help would be appreciated.
Jenkins runs the following commands:
cp -R vendor/build.gradle platforms/android/build.gradle
ionic resources
ionic prepare android
ionic build android --release
jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore $KEYSTORE platforms/android/build/outputs/apk/android-release-unsigned.apk
-keypass $KEY_PASSWORD -storepass $KEYSTORE_PASSWORD $ALIAS && \ ./zipalign -v 4 platforms/android/build/outputs/apk/android-release-unsigned.apk "build/appNameHere.apk"
While 'ionic build android --release' eventually calls ./platforms/android/cordova/lib/builders/GradleBuilder.js which has several lines referencing build.gradle, but I think the main most vital one being var buildGradle = fs.readFileSync(path.join(this.root, 'build.gradle'), 'utf8'); in which it reads in the file to use with GradleBuilder.build:
GradleBuilder.prototype.build = function(opts) {
var wrapper = path.join(this.root, 'gradlew');
var args = this.getArgs(opts.buildType == 'debug' ? 'debug' : 'release', opts);
return Q().then(function() {
return spawn(wrapper, args, {stdio: 'inherit'});
});
};

After much trial and error, the answer turned out to be that I needed this block of code to be located in a different segment of my build.gradle file.
release {
signingConfig signingConfigs.release
debuggable false
minifyEnabled true
proguardFile 'customProGuard.txt'
proguardFiles getDefaultProguardFile('proguard-android.txt'),
'proguard-android.txt'
}
As it was, it was never being reached. By putting it in the android { } block, but not in any other functions or if statements, it consistently runs and behaves as expected.
Unfortunately, due to the way Ionic uses reflection, I'm not able to get much obfuscation use out of it (Apache Jira Bug). ProGuard can still be used to remove the console logs as per the linked article above though.

Related

Why does the order of `buildTypes` matter?

I'm running into a strange issue with gradle.
My app/build.gradle file has a section which looks like this:
buildTypes {
debug {
...
}
release {
signingConfig signingConfigs.release
...
}
beta {
initWith release
...
}
}
When I run ./gradlew assembleBeta, the output is app-beta.apk, which I can install on my device.
However, I want to change the order of my build types, purely for aesthetics. So it now looks like:
buildTypes {
debug {
...
}
beta {
initWith release
...
}
release {
signingConfig signingConfigs.release
...
}
}
This syncs fine, but when I run ./gradlew assembleBeta, the output is now app-beta-unsigned.apk! And I cannot install this apk on a device - it tells me installation failed.
How is it possible that simply by changing the order in which my build types are declared, my apk can become broken?
The reason the apk is broken after changing the order of build types is because, in this situation, gradle requires that the release build type is defined before the beta build type.
Why is this? It's because beta refers to release in the line: initWith release
If release is defined after beta, then the initWith release command will silently fail. This is because, at the point initWith release is run, release does not exist. Although it may seem like Gradle should be able to look-ahead to the definition of release later in the file, it cannot.
Then, when running ./gradlew assembleBeta, the beta apk is built successfully - but the apk is unsigned - as alluded to by the name of the output file, app-beta-unsigned.apk. In the previous setup, beta was getting its signingConfig from release. When initWith release fails, the beta build simply has no signingConfig set - so the output apk is of course unsigned.

How to view Flutter app Dart stacktrace with Firebase Crashlytics?

I have a flutter app that uses some Android-specific code (Java/Kotlin). In "app/build.gradle" i do have firebaseCrashlytics/mappingFileUploadEnabled true:
release {
signingConfig signingConfigs.googleplaySigningConfig
minifyEnabled true
debuggable false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard.cfg'
firebaseCrashlytics {
mappingFileUploadEnabled true
nativeSymbolUploadEnabled true
unstrippedNativeLibsDir "build/app/intermediates/merged_native_libs/release/out/lib"
}
ndk {
debugSymbolLevel 'FULL'
}
}
Also note in root "build.gradle":
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.8.1'
I build the app using flutter build appbundle and i believe it should trigger uploading of obfuscation maps to Firebase crashlytics.
However i get smth like:
which makes me think Firebase Crashlytics was unable to find deobfuscated code for obfuscated using the uploaded mapping.
What can i do or check?
PS. After reading the error message more carefully i realized it's actually Dart code where the error happens (not my Java code). However, i'm still confused why i can't see Dart stacktrace instead.

"Unexpected attempt to get register for a value without a register" during release builds but not debug

Debug builds work fine for me. When I choose Active Build Variant = release, and try to run Build -> Generate Bundle(s) / APK(s) -> Build APK, the build runs for a while, then I get the following error:
Unexpected attempt to get register for a value without a register in method java.util.List com.chrynan.chords.parser.AsciiChordParser.parseLineAsString(java.lang.String, int, java.util.Set).
That is referencing an external library I'm pulling in. Source code for that function is available here.
What does that error mean? My searches returned nothing remotely like it.
I solved it! I have no idea why, but I set minifyEnabled = true in my build.gradle (:app):
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
This gave me instant crashing on app start due to this problem, although I think that's unrelated. I fixed that, and now my build works.
I still have no idea why that error came up though.
For some one else having the same problem, I solved it by changing
getDefaultProguardFile( 'proguard-android-optimize.txt') to
getDefaultProguardFile( 'proguard-android.txt') in build.gradle

Buck/OkBuck: DexGuard integration not working

I have been using DexGuard successfully with Gradle in Android Studio without any issues. I recently tried my hands on OkBuck to speed up my build time and it really helped me.
Although it is able to build debug and signed APKs for me, but when I try building for release with DexGuard like:
./buckw install --run app:bin_release
I get the following error:
Error: Unknown option '-dalvik' in line 9 of file 'SomeApp/app/build/okbuck/release/proguard.pro',
included from line 60 of file 'buck-out/gen/app/bin_release/proguard/command-line.txt',
included from argument number 1
BUILD FAILED: //app:bin_release failed with exit code 1:
proguard_obfuscation
stderr: Error: Unknown option '-dalvik' in line 9 of file 'SomeApp/app/build/okbuck/release/proguard.pro',
included from line 60 of file 'buck-out/gen/app/bin_release/proguard/command-line.txt',
included from argument number 1
It is probably insignificant to mention the details of DexGuard integration as it is done as per the documentation and is working fine when I build from within Android Studio or using ./gradlew, but here it is:
SomeApp/build.gradle:
buildscript {
ext {
DEXGUARD_HOME = "$System.env.DEXGUARD_HOME"
}
...
SomeApp/app/build.gradle:
buildTypes {
...
release {
minifyEnabled true
proguardFile DEXGUARD_HOME + "Dexguard-7.3.11/lib/dexguard-release-aggressive.pro"
proguardFile 'dexguard-project.txt'
signingConfig signingConfigs.release
}
}
The message typically indicates that ProGuard is still enabled -- ProGuard doesn't know the DexGuard option -dalvik. You should leave minifyEnabled set to false. DexGuard itself already shrinks, optimizes, and obfuscates all code and resources.

Does zipalign execute automatically when a .apk is generated as signed from Android Studio?

I am wondering if the method of generating a signed .apk for release , also zipaligns the apk . In the directions of the android page (http://developer.android.com/tools/publishing/app-signing.html#studio) , it is not clear if zipalign is a different step.
thank you!
You can define build types in the build.gradle file.
buildTypes {
debug {
storeFile file("debug.keystore")
}
release {
zipAlignEnabled true
}
}
The debug config is used when you are debugging your apk and the release config when you're creating a release apk.
If you set zipAlignEnabled true in case of release, the apk will be zipaligned. If you do not specify it, the default value is true in case of release and the apk will be zipaligned automatically. For debug, the default value is false.
Read more about build types and other possible properties you can set here : http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Types
The Android build tools can handle this for you. Android Studio automatically aligns your APK.
use the below command to confirm the alignment of existing.apk:
zipalign -c -v <alignment> existing.apk
The is an integer that defines the byte-alignment boundaries. This must always be 4 (which provides 32-bit alignment) or else it effectively does nothing.
Flags:
-f : overwrite existing outfile.zip
-v : verbose output
-p : outfile.zip should use the same page alignment for all shared object files within infile.zip
-c : confirm the alignment of the given file
Zipalign

Categories

Resources