Do #NonNull and #Nullable annotations effect the performance in Android during runtime? I mean they only have the purpose to support LINT and other tools to detect possible bugs, so they probably/hopefully will be ignored during compiling. Do you have any sources for me proving this?
The #NonNull and Nullable annotations are declared with CLASS retention policy. They are seen in the compiled bytecode but can be ignored by the VM at runtime.
If you're concerned about performance, run an experiment to see if it is an issue for you. I would hypothesize that class-retention annotations don't add any significant overhead.
(If the retention policy was runtime and the annotations were queried via reflection at runtime, then there would be performance issues especially in the past.)
As a little experiment I created the following method in an Android app:
private void test(#NonNull Object o) {
o.toString();
}
Compiled the project, 'un-compiled' it with ApkTool and took a look at the smali file:
.method private test(Ljava/lang/Object;)V
.locals 0
.param p1, "o" # Ljava/lang/Object;
.annotation build Landroid/support/annotation/NonNull;
.end annotation
.end param
.prologue
.line 22
invoke-virtual {p1}, Ljava/lang/Object;->toString()Ljava/lang/String;
.line 23
return-void
.end method
So the Nonnull annotation is not actually removed from the code at compile time.
I could though turn off the checks in Android studio, call the test-method with null as an argument and compile the app. It then crashes with a NullPointerExeption at o.toString() of course, but there is no hint of the VM actually using the annotation to check the parameter at runtime. Exactly what laalto said in his answer.
I do wonder though if the VM would be capable of doing so and throwing an exception when the method is called with the null argument.
Related
I am contributor for a shared library project that just received notice one of our customers received this warning from the Google Play Store,
Hello Google Play Developer, In July, the app submissions listed at
the end of this email were rejected due to an unsafe implementation of
the WebViewClient.onReceivedSslErrorHandler. This implementation
ignores all SSL certificate validation errors, making your app
vulnerable to man-in-the-middle attacks. An attacker could change the
affected WebView's content, read transmitted data (such as login
credentials), and execute code inside the app using JavaScript.
When looking into our AuthorizationWebViewClient which extends WebViewClient, we do not implement onReceivedSslErrorHandler, meaning we fall under the default implementation putting this library in the clear.
Can we get confirmation if this is a false positive or if there are changes that have to be made to this library?
UPDATE:
This was due to the version of the SDK used being much older when this vulnerability was present, the current codebase does not have this issue hence the disconnect.
I'm not sure what the specific app in question is, but there are apps which have a vulnerable com.microsoft.services.msa.AuthorizationRequest$OAuthDialog$AuthorizationWebViewClient.
For example, disassembling the code of one app shows that it does in fact have an onReceivedSslErrorHandler implementation.
.method public onReceivedSslError(Landroid/webkit/WebView;Landroid/webkit/SslErrorHandler;Landroid/net/http/SslError;)V
.registers 4
.param p1, "view" # Landroid/webkit/WebView;
.param p2, "handler" # Landroid/webkit/SslErrorHandler;
.param p3, "error" # Landroid/net/http/SslError;
.prologue
.line 143
invoke-virtual {p2}, Landroid/webkit/SslErrorHandler;->proceed()V
.line 144
return-void
.end method
After compiling a certain apk and going through the smali code I stumbled upon this line in file com/name/name/r.smali:
invoke-static {v1, v0, v2}, Lcom/name/name/p;->a(Ljava/lang/String;Ljava/util/List;Lcom/name/name/CallbackI;)V
name is a placeholder in this situation
Unfortunately both of the classes p and CallbackI don't exist in com/name/name, how is this possible?
There are a few possibilities:
The code referencing those missing classes is dead code that never gets executed.
The app crashes when it tries to run that code.
The classes are defined elsewhere. Is the app using multi-dex? Or maybe the app uses a DexClassLoader to load another dex file?
I'm looking into some Android performance issues at the moment and noticing some sub-optimal patterns in the dex code. I'm just wondering if anyone knows if this is to be expected, and what the rationale behind it might be.
For example, consider the following Java code:
m_testField += i;
doSomething(m_testField);
When this is built and then run through baksmali it looks like the following:
iget v1, p0, Lcom/example/MainActivity$FieldTest;->m_testField:I
add-int/2addr v1, v0
iput v1, p0, Lcom/example/MainActivity$FieldTest;->m_testField:I
iget v1, p0, Lcom/example/MainActivity$FieldTest;->m_testField:I
invoke-direct {p0, v1}, Lcom/example/MainActivity$FieldTest;->doSomething(I)V
The part that's concerning me is the iget opcode to read the value of the instance field into register v1. The same field was written from the very same v1 register in the preceding opcode, so the opcode would appear to be completely redundant.
The only thing I can think of is that this is done to make this more thread-safe. But surely that should be the programmer's responsibility (by using sync blocks) instead of the compiler's responsibility. Although I'm not 100% certain, I think the above behaviour is quite different to what most C/C++ compilers would do.
I should say that essentially the same dex is produced when ProGuard is used. I should also probably mention that I'm using the very latest Android tools and a late model JDK.
Every access to a field is independent. To get the behavior you describe, you need to add an extra local variable:
int local = m_testField; // iget
local = local + i;
m_testField = local; // iput
doSomething(local);
That said, some combination of the interpreter, just-in-time compiler and ahead-of-time compiler may end up making these optimizations for you at runtime anyway.
On a hunch, I've done some further research and I think I'm in a position to answer my own question...
The sub-optimal dex seems to be a by-product of the fact that it is generated from standard Java bytecode which is stack-based rather than register-based. I disassembled the .class file corresponding to the sample code in my question. The relevant section looks like this:
5: aload_0
6: dup
7: getfield #22 // Field m_testField:I
10: iload_1
11: iadd
12: putfield #22 // Field m_testField:I
15: aload_0
16: aload_0
17: getfield #22 // Field m_testField:I
20: invokespecial #33 // Method doSomething:(I)V
After the iadd opcode on line 11 is executed, the value of m_testField is at the top of the stack and the 'this' reference is second from the top. The problem is that the putfield opcode on line 12 removes these from the stack. This means that the field value has to be re-pushed to the stack on line 17.
I must say I'm pretty surprised by this inefficiency. I'd have thought that the dx tool that converts bytecode to dex would be clever enough to remove this redundancy. I'm just hoping that ART is clever enough to do this at runtime instead.
I diassembled a simple android app using apktool and it generated some smali codes.other things are understandable but i am not getting the meaning of .prologue in the smali code.please help me
Here other variables are self understanding linke invoke and locals but what does this .prologue do??
# direct methods
.method public constructor <init>()V
.locals 0
.prologue # What does this do?
.line 17
invoke-direct {p0}, Landroid/app/Activity;-><init>()V
return-void
.end method
This is equivalent to the DBG_SET_PROLOGUE_END debug opcode in the dex file, as documented here.
sets the prologue_end state machine register, indicating that the next
position entry that is added should be considered the end of a method
prologue (an appropriate place for a method breakpoint). The
prologue_end register is cleared by any special (>= 0x0a) opcode.
I encountered #SuppressLint("InlinedApi") in some code i was going through and could not find out any description of it online. I understand #SuppressLint("NewApi") is used to hide warnings when we write code that is higher than the minsdk mentioned in the manifest. But i am not able to figure out when "InlinedApi" should be used. Any ideas?
By executing lint --list (the lint tool is located in your sdk/tools directory) you can see a list of the valid issue id's. You can find the explanation of InlinedApi there :
"InlinedApi": Finds inlined fields that may or may not work on older
platforms
Here's an example from a Google codelab:
#SuppressLint("InlinedApi")
private void hideSystemUi() {
mPlayerView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE
| View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
}
If you comment out the #SuppressLint("InlinedApi"), you get this lint warning:
Field requires API level 19 (current min is 16): android.view.View#SYSTEM_UI_FLAG_IMMERSIVE_STICKY
So you're accessing a field that may not exist in the API of some of the devices that you've said you want to be able to run the device on. In that case, why is it just a lint warning instead of a fatal compile error?
The fuller description for the warning is nice and informative. You can see it in Android Studio if you press the "More" key combo (e.g. Cmd+F1) when the lint message popup is open. You can also get it via lint on the command line, similar to what #stan0 said but in more detail:
lint --show InlinedApi
Here's the detailed explanation:
InlinedApi
----------
Summary: Using inlined constants on older versions
Priority: 6 / 10
Severity: Warning
Category: Correctness
This check scans through all the Android API field references in the
application and flags certain constants, such as static final integers
and Strings, which were introduced in later versions. These will
actually be copied into the class files rather than being referenced,
which means that the value is available even when running on older
devices. In some cases that's fine, and in other cases it can result
in a runtime crash or incorrect behavior. It depends on the context,
so consider the code carefully and decide whether it's safe and can be
suppressed or whether the code needs to be guarded. [emphasis added]
If you really want to use this API and don't need to support older
devices just set the minSdkVersion in your build.gradle or
AndroidManifest.xml files. If your code is deliberately accessing
newer APIs, and you have ensured (e.g. with conditional execution)
that this code will only ever be called on a supported platform, then
you can annotate your class or method with the #TargetApi annotation
specifying the local minimum SDK to apply, such as #TargetApi(11),
such that this check considers 11 rather than your manifest file's
minimum SDK as the required API level.
(source)
Hopefully with that explanation, it's clear why this is not a fatal error (because the value of the constant gets copied into the class file instead of a reference), why it's still potentially dangerous, and when to suppress the warning. In the codelab example above, the author apparently decided that adding a flag that wouldn't be recognized on older devices was safe. Maybe he had information that unrecognized flags would be silently ignored, though I don't see that in the documentation.
I found this..
#SuppressLint("InlinedApi")
Indicates that Lint should ignore the specified warnings for the annotated element.
Exp:
SuppressLint
implements from Annotation Class.
android.annotation.SuppressLint like this..
Built-In Annotations
Java defines a set of annotations that are built into the language
Annotations applied to java code:
#Override - Checks that the method is an override. Causes a compile error if the method is not found in one of the parent classes or implemented interfaces.
#Deprecated - Marks the method as obsolete. Causes a compile warning if the method is used.
#SuppressWarnings - Instructs the compiler to suppress the compile time warnings specified in the annotation parameters
http://developer.android.com/reference/java/lang/annotation/Annotation.html
http://developer.android.com/reference/android/annotation/SuppressLint.html