We use ASM to rewrite Java bytecode in jar
files during the build process, which usually happens after java compilation of the specific jar
file and before the final assembly of apk
file. Through bytecode rewrite, we can rewrite/redirect classes, methods, etc. without patching or manually overriding every occurance of them.
In order to use bytecode rewrite, you need to take the following steps:
- Start by making a file and a class named
{***}ClassAdapter
underbuild/android/bytecode/java/org/brave/bytecode
. The folder has a lot of examples of how the class shall be like. The most important part is it shallextends BraveClassVisitor
and all redirections/rewrites happen in its constructor. - Go to
build/android/bytecode/java/org/brave/bytecode/BraveClassAdapter.java
and add your newly created class to the chain of invocation of the adapters. - Go to
build/android/bytecode/BUILD.gn
and add your newly createdjava
file tosources
. - If you are redirecting/rewriting any exisitng classes, methods or fields from upstream, you shall add tests for their existence in
android/javatests/org/chromium/chrome/browser/BytecodeTest.java
. In addition, if you are redirecting a class, also add tests for consturctors and supers. - If you are redirecting an existing method, add that method to
android/java/proguard.flags
following the examples of exising entries. The***
and...
part means the rule will match any method signature regardless of what types and how many parameters there are. - Check any references to the classes/methods/fields you want to rewrite is compiled to one of the
jar
files listed inbuild/android/bytecode/bytecode_rewriter.gni
. If not, then add the outputjar
file to the list. The easiest way to find out which jar file is search for the name of the sourcejava
file you are trying to rewrite ingn/gni
files and find theandroid_library
target which compiles the file. Thejar
file shall be the one with the same target name underobj/{path to the gn file you are looking at}
.
-
changeSuperName(String className, String superName)
: change super ofclassName
tosuperName
. -
deleteMethod(String className, String methodName)
: deletemethodName
fromclassName
. -
makePublicMethod(String className, String methodName)
: change the visibility ofmethodName
inclassName
topublic
. -
makePrivateMethod(String className, String methodName)
: change the visibility ofmethodName
inclassName
toprivate
. -
changeMethodOwner(String currentOwner, String methodName, String newOwner)
: change any invocation ofmethodName
from being from classcurrentOwner
to classnewOwner
. This does not change method definition. -
deleteField(String className, String fieldName)
: deletefieldName
fromclassName
. -
deleteInnerClass(String outerName, String innerName)
: delete and inner classinnerName
from classouterName
. -
makeNonFinalClass(String className)
: remove keyworkfinal
from the definition ofclassName
. -
makePublicInnerClass(String outerName, String innerName)
: make the visibility of inner classinnerName
insideouterName
to bepublic
. -
makeProtectedField(String className, String fieldName)
: change the visibility offieldName
inclassName
toprotected
. -
addMethodAnnotation(String className, String methodName, String annotationType)
: add annotationannotationType
(e.g.Override
) to the definition ofmethodName
. -
redirectConstructor(String originalClassName, String newClassName)
: redirect any invocation of constructororiginalClassName
(i.e.new originalClassName()
) to a new constructornewClassName
. -
redirectTypeInMethod(String className, String methodName, String originalTypeName, String newTypeName)
: change any mention of type in definition ofmethodName
inclassName
fromoriginalTypeName
tonewTypeName
. This includes method signature, body and return type.