How to prevent "Call requires API level" from 3rd party libraries - android

App Code
When we use some code in our Android application that does not exist in the min-version SDK, Android Studio will show an error about it.
Example:
Let's assume my app has target SDK 26 and min SDK 21 and my code tries to call Objects.isNull(var);: then we get a nice error-message:
Call requires API level 24 (current min is 21): java.util.Objects#isNull
AFAIK this is a Lint message (not a compiler warning or error).
Third party code
How can we have the same checks for 3rd party java libraries?
Example:
when the project depends on a lib.jar file and this library internally uses Objects.isNull, then we don't get any error/warning and the app will crash on older devices.
One way to avoid this is to make Instrumentation Tests - but these tests are slow and you never have 100% code coverage.
e.g. I actually thought that ProGuard should report this issue. But it doesn't.
Maybe because it uses the target Android SDK min SDK?
So what can we do about this in the following 2 situations:
we must use the 3rd party library as it is
we can fork the 3rd party library and can use this fork: what could we do to find/avoid these errors: e.g. should we convert it to an aar?

How can we have the same checks for 3rd party java libraries?
Add the source code to your project.
we must use the 3rd party library as it is
Ask its developers what the relevant minSdkVersion is for the library, then set yours to match, or otherwise avoid calling their library on older devices.
we can fork the 3rd party library and can use this fork: what could we do to find/avoid these errors: e.g. should we convert it to an aar?
Add the source code to your project. Or, create some other project with the source code. Lint checks source code, not compiled code. If you create some other project with the source code, you would need to have it compile a JAR or AAR and make that available to your main project.

Related

Android 11 JNI disable fdsan

I have an App that uses a library with native libs and now in Android 11 is crashing due to fdsan.
I have contacted the library providers, but they are not going to deliver a new version of the library with this issue solved, so I need to be able to somehow disable fdsan.
I think the solution would be to call when I am on Android 11 or higher:
if(android_get_device_api_level()>=30)
android_fdsan_set_error_level(ANDROID_FDSAN_ERROR_LEVEL_WARN_ONCE);
But I am unable to compile because of error:
use of undeclared identifier 'android_fdsan_set_error_level'
Because this was only added in Android 10 (API 29)
So my question is how can I call this function in my project?
I am creating a specific cpp file for this and a specific library for this, so I would need to some how for this library say that it is only for Android 10 or higher.
Is there some definition in the cpp file or in the Android.mk file that I can use to be able to compile this specific library that I will invoke from Java in Android 11 or higher only when my App starts?
According to the documentation here you should do it like this:
if (android_fdsan_set_error_level) {
android_fdsan_set_error_level(ANDROID_FDSAN_ERROR_LEVEL_WARN_ONCE);
}
You will have to set your target SDK to 10 or above for this to work.
Alternatively, if you are compiling this in to a separate native binary, you can use conditional compilation.
Include this file.
Then do something like this:
#if __ANDROID_API__ >= __ANDROID_API_P__
android_fdsan_set_error_level(ANDROID_FDSAN_ERROR_LEVEL_WARN_ONCE);
#endif
Just make sure this binary is never loaded on older devices, else the linker will give an error.
Depending on how your whole project is built, you may need to ship a separate APK for this in a bundle.
Finally, if all else fails, you may try using dlopen and dlsym to load the function dynamically at runtime, but you will need to find out which module exactly it is included in.

Unable to load libnative-activity.so when CMAKE_BUILD_TYPE is set to anything but debug

After updating NDK to version 19 my application throws a UnsatisfiedLinkError with this message "Unable to load native library "/data/app/com.company.appname-SkXFrxADQ610MNjJr7Ak3A==/lib/arm64/libnative-activity.so": null" at start, and then terminates.
Anyone knows what is causing this error, and how to resolve it?
Notes:
1) When CMAKE_BUILD_TYPE is set to Debug, everything works fine, regardless of optimization (-O) level or DEBUG_MODE setting.
2) This did not happen prior to NDK 19
3) I do call app_dummy(), even though the call is deprecated and not necessary
4) The application uses android_native_app_glue.h/.c i.e no Java at all.
5) The exception libname is "native-activity", and funcname is "ANativeActivity_onCreate"
Update:
From the NDK r19 revision history Google says ndk-depends has been removed. We believe that ReLinker is a better solution to native library loading issues on old Android versions. I suppose this is relevant, guessing ndk-depends preserved the native activity, but as a native glue application does not contain any java code, how can I apply ReLinker from code?
fread_unlocked isn't available until Android P. You can't use that function in an app with a minSdkVersion lower than 28.
This shouldn't have compiled unless you've given CMake an incorrect ANDROID_PLATFORM, as the symbol wouldn't have been available to link against. If you're building your app with gradle and externalNativeBuild then gradle configures this for you based on your minSdkVersion. If you're not, you should probably switch to using it as it provides these safeguards.

Android Nougat: Detected problems with app native libraries

we just upgrade our Nexus 96 to Android N and now get the following popup while working with our app:
give me some suggestions
Check reported same issue:
https://github.com/litehelpers/Cordova-sqlcipher-adapter/issues/41
Now it has already been resolved.
For reference:
sqlcipher/android-database-sqlcipher#216
You can check SQLCipher for Android Release—Android N Support on below link:
https://discuss.zetetic.net/t/sqlcipher-for-android-release-android-n-support/1465
EDIT:
You can also check
NDK Apps Linking to Platform Libraries for private libraries usage.
Check "Update your app" section which provides steps to fix these types of errors.
You are probably using a native library that is directly accessing private APIs. The issue is described below.
From Android Developers Blog https://android-developers.googleblog.com/2016/06/android-changes-for-ndk-developers.html:
Private API (Enforced since API 24)
Native libraries must use only public API http://developer.android.com/ndk/guides/stable_apis.html?utm_campaign=android_discussion_ndkchanges_062716&utm_source=anddev&utm_medium=blog, and must not link against non-NDK platform libraries. Starting with API 24 this rule is enforced and applications are no longer able to load non-NDK platform libraries. The rule is enforced by the dynamic linker, so non-public libraries are not accessible regardless of the way code tries to load them: System.loadLibrary(...), DT_NEEDED entries, and direct calls to dlopen(...) will fail in exactly the same way.
(...)
Potential problems: starting from API 24 the dynamic linker will not load private libraries, preventing the application from loading.
Resolution: rewrite your native code to rely only on public API. As a short term workaround, platform libraries without complex dependencies (libcutils.so) can be copied to the project. As a long term solution the relevant code must be copied to the project tree. SSL/Media/JNI internal/binder APIs should not be accessed from the native code. When necessary, native code should call appropriate public Java API methods.
A complete list of public libraries is available within the NDK, under platforms/android-API/usr/lib.
As other answers pointed, it seems that this API 24 issue has been solved.

How to check the Support Library / AppCompat library version at Runtime?

We have an SDK for Android that requires Support Library v4 with targetSdk 23 (meaning that the Support Library should be also 23). For commercial reasons, I have to upgrade the SDK so it should work even if the App is compiled against API 21.
This means that some calls to the Support Library should be done conditionally depending on the current Support Library version of the App (not the current API level), so I have 3 options here (ranked by best to worst):
1 - Check the Support Library version before the method call (This is what I have not figured out how to do, (if possible at all)).
2 - Use reflection.
3 - Use Try Catch blocks.
So for the 1st option, is it possible to check the support library version at runtime?
I'll answer my question with the approach I finally chose:
It is not possible to check the Support Library / AppCompat library revisions at Runtime.
Typically you already know which version of the Support Library / AppCompat Library version you are going to use, as it is declared explicitly in the App dependencies. However, due that in this case the SDK code should run under any condition (or at least a min version of the libraries), some different things were done in order to solve the problem:
1 - Check method availability using Java Reflection.
2 - Invoke methods using also reflection and try-catch blocks (in order to ensure App stability)
3 - Determine the AppCompat version using the info provided by the catch block.
4 - Subsequent calls don't use reflection as existence of the methods has been already checked, and min version has been already found.

In Android, how to force the native code to use my version of a shared library, instead of the system's?

Android already has the PCRE shared library ("/system/lib/libpcre.so"), but it's compiled without Unicode support, so I've built my own version of PCRE for Android. My native code is linked to the PCRE shared library. However, when I run my app, it uses the system's version of PCRE, rather than the one I've built, even though my APK does include my version of PCRE. How do I make it use my version of PCRE rather than the system's? Generally in Linux I use "LD_LIBRARY_PATH" or "RPATH", but on Android I don't know how to use them, if that's at all possible.
You can load("your-path-to-lib") before you load the main library. With this API you can load a native library at arbitrary location. But the easiest way is to rely on the default behavior of build and installer, which will pack the native libraries (named libsomething.so) that it finds in libs/<ABI> folders, into the APK file, and unpack the ABI variant that matches the target into
getContext().getApplicationInfo().nativeLibraryDir
(this was added in API level 9)
If the library is pre-loaded, you cannot have your library side-by-side with the system one, due to a bug that invloved older versions of Android. Still, you may succeed to unload it manually, using dlclose():
handle = dlopen("<libname>", RTLD_NOLOAD);
dlclose(handle);
dlclose(handle); // twice, because dlopen() increments ref count
You will probably do these manipulations in a separate small dlclose_helper.so. Load this helper load before you load the main library, which needs the private version of <libname>.
A fix was introduced for API level 23 that lets us finally load both dir1/libx.so and dir2/libx.so (see the official doc).
Note that for API level 24, new restrictions have also been introduced that limits access to system libraries (you can only load white-listed ones).
I believe the only way is to rename the library (libpcre_myapp, for example).
Note that renaming just the file probably is not sufficient, but changing the SO_NAME ELF property.

Categories

Resources