• The parenting of Loading Native Libraries on Android
  • Original address: medium.com/keepsafe-en…
  • Hilal Alsibai
  • Original publication time: November 7, 2015

Last year, THE author encountered a problem: the installation of the project was normal after the project was packaged as apK file. After the project was packaged as AAB file, the MMKV initialization crashed after the project was parsed into APK file through bundleTool. However, after the AAB file was uploaded to googlePlay, the project was parsed into APK file and downloaded and installed normally.

By reading this article, you can learn why this problem occurs and how to solve it.

The translation

Back in 2012, in the early days of KeepSafe, we tried to implement an encryption scheme for our Android applications. Through many iterations and prototypes, we found a sweet spot by leveraging the power of JNI (Java native Interface). We decided to write the interface into the cryptographic library we use in Java, which is called through JNI for encryption and decryption purposes only. We chose an immediate solution to minimize the impact on the user experience. Once we were satisfied with our solution, we decided to deploy it into our production application. We rigorously tested our code and were confident that everything would work well, but things turned out to be out of our control.

UnsatisfiedLinkErrorNow in the early

As we anxiously updated crash reports after launch, we began to notice a recurring bug. The user is experiencing an UnsatisfiedLinkError, which means there are two possibilities :(1) the local library we called does not exist; (2) The native method we called does not exist. Since the second is almost always caught by compilation and basic testing, at this point we are confused by the fact that the user’s installation does not have the so native library that we provide in APK.

Here are some log files:

java.lang.UnsatisfiedLinkError: Couldn't load stlport_shared from loader dalvik.system.PathClassLoader[dexPath=/data/app/com.kii.safe-1.apk,libraryPath=/data/app-lib/com.kii.safe-1]: findLibrary returned null\ at java.lang.Runtime.loadLibrary(Runtime.java:365)\ at Java. Lang. System. LoadLibrary (535) System. Java: \ at com kii. Safe. Native. < clinit > (Native) Java: 16) \... 63 more Caused by: java.lang.UnsatisfiedLinkError: Library stlport_shared not found\ at java.lang.Runtime.loadLibrary(Runtime.java:461)\ at Java. Lang. System. LoadLibrary (557) System. Java: \ at com kii. Safe. Native. < clinit > (Native) Java: 16) \... 5 more Caused by: java.lang.UnsatisfiedLinkError: Cannot load library: get_lib_extents[760]: 1305 -- / MNT /asec/com.kii.safe-1/lib/libstlport_shared.so is not a valid ELF object\ at java.lang.Runtime.loadLibrary(Runtime.java:434)\ at java.lang.System.loadLibrary(System.java:554)\ at com.kii.safe.Native.<clinit>(Native.java:15) Caused by: java.lang.UnsatisfiedLinkError: Library cryptopp not found\ at java.lang.Runtime.loadLibrary(Runtime.java:461)\ at java.lang.System.loadLibrary(System.java:557)\ at com.kii.safe.Native.<clinit>(Native.java:17)Copy the code

It’s infuriating

Caused by: java.lang.UnsatisfiedLinkError: Cannot load library: reloc_library[1286]: 1748 always locate '쯰 ҷ Ц f1 Ϙ ˗ ˞ ք ᣼ 0 Ⱉ ض 夘 Ϛ. ͏ ð « ” ¶ 㥁 ج 뭫 ර ⓻ kind guide ^ ӎ ` 3 c + W - B ַ ˌ ֕  꼠 '... \ at java.lang.Runtime.loadLibrary(Runtime.java:370)\ at java.lang.System.loadLibrary(System.java:535)\ at com.kii.safe.Native.<clinit>(Native.java:17) Caused by: java.lang.UnsatisfiedLinkError: Cannot load library: reloc_library[1312]: 1327 always locate the P # ܝ ׶ ʭ X Ϛ ߐ ܝ # ׶ ʭ X Ϛ ߐ ܝ # ׶ ʭ X Ϛ ߐ ܝ # ׶ ʭ X Ϛ ߐ ܝ # ׶ ʭ X Ϛ ߐ ܝ # ׶ ʭ X  ߐ ܝ # ׶ ʭ X Ϛ ߐ ܝ # ׶ ʭ X Ϛ ߐ ܝ # ׶ ʭ X Ϛ ߐ ܝ # ׶ ʭ X Ϛ ߐ ܝ # ׶ ʭ X Ϛ ߐ ܝ # ׶ ʭ X Ϛ ߐ ܝ # ׶ ʭ X Ϛ ߐ ܝ # ׶ ʭ X Ϛ ߐ ܝ # ׶ ʭ X Ϛ ߐ ܝ # ׶ ʭ X Ϛ ߐ ܝ # ׶ ʭ X Ϛ ߐ ܝ # ׶ ʭ X Ϛ ߐ ܝ # ׶ ʭ X Ϛ ߐ ܝ # ׶ ʭ X Ϛ ߐ ܝ # ׶ ʭ X Ϛ ߐ ܝ # ׶ ʭ X Ϛ ߐ ܝ # ׶ ʭ X Ϛ ߐ ܝ # ׶ ʭ X Ϛ ߐ ܝ # ׶ ʭ X Ϛ ߐ ܝ # ׶ ʭ X Ϛ ߐ ܝ # ׶ ʭ X Ϛ ߐ ܝ # ׶ ʭ X Ϛ ߐ ܝ # ׶ ʭ X Ϛ ߐ ܝ # ׶ ʭ X Ϛ ߐ ܝ # ׶ ʭ X Ϛ ߐ ܝ # ׶ ʭ X Ϛ ߐ ܝ # ׶ ʭ X Ϛ ߐ ܝ # ׶ ʭ X Ϛ ߐ ܝ # ׶ ʭ X Ϛ ߐ ܝ # ׶ ʭ X Ϛ ߐ ܝ # ׶ ʭ X Ϛ ߐ ܝ # ׶ ʭ X  ߐ ܝ # ׶ ʭ X Ϛ ߐ ܝ # ׶ ʭ X Ϛ ߐ ܝ # ׶ ʭ X Ϛ ߐ ܝ # ׶ ʭ X Ϛ ߐ ܝ # ׶ ʭ X Ϛ ߐ ܝ # ׶ ʭ X Ϛ ߐ ܝ # ׶ ʭ X Ϛ ߐ ܝ # \ at Java lang. Runtime. The loadLibrary (434). The Runtime Java: \ the at java.lang.System.loadLibrary(System.java:554)\ at com.kii.safe.Native.<clinit>(Native.java:17)Copy the code

There is no clear pattern as to which library is problematic, because it seems that all so libraries are throwing exceptions. It’s not Specific to the Android version, it doesn’t just happen on a particular device. Also, in some cases, some local hangars are loaded correctly, but not all. At this point, we frantically search the Internet for answers or help, only to come back empty-handed. We started releasing fixes, mostly speculative fixes, additional trace data to better understand the exact environment in which the crash occurred, and a piece of code that specifically examines the local hangar at the intended installation location.

Also, in some cases, some local hangars are loaded correctly, but not all.

The information we collected indicates that the local library does not exist. A system class or file system error is not a strange fluke, but the user’s device looks fine. Here are a few ideas:

(1) User device space is insufficient

When we considered the possible cause of this anomaly, we began to think that maybe people simply didn’t have the space to install the hangar properly, and therefore it was never installed. A quick diagnostic check before trying to load the library quickly proved the idea wrong. Users have enough space on their devices to hold the libraries we are shipping.

(2)soLibraries are not included in the update

The second possible cause of our problem was that Google Play broke our APK when it was delivered to user devices. After reading reports like this one, we gained some support for the idea, detailing that Google Play should notify all app developers affected by the issue, which caused users to be unable to launch their apps after an update because the native libraries were not installed correctly. The only problem was that the report came out in August and we didn’t start dealing with it until a few months later. We never received a notification from Google Play that they had any errors. Of course, this is hard to test.

(3) Debug problems directly with real users

Since we couldn’t reproduce the problem on the more than 10 different devices we had, we decided to contact users who were experiencing the problem. One user generously decided to help us out and pointed out that the application was working fine until the latest update. The problem here is that the version of the application that users notice is one that contains both our encryption code and our own library, just to add to our confusion and overall puzzle. We decided to provide the user directly with an APK file where we verified that all local libraries existed in the APK file. The user installs APK, starts the application, and then directly encounters the same UnsatisfiedLinkError exception. This confirms that the problem is not Google Play, but Android’s PackageManager installation process.

Find a solution

Because we found that the problem was in the installation process, we decided to duplicate the part of the installation process that extracted the local library from the application code. Fortunately, you can easily get a reference to an application’s APK file on your device by following the steps below

Context.getApplicationInfo().sourceDir;
Copy the code

We will then use it to extract the local library to an internal storage location. Since APK files are just ZIP files, you only need to write some ZIP extraction code. We were able to extract code and ship it quickly, resulting in a significant drop in crashes! The total number of UnsatisfiedLinkError exceptions thrown daily is shown as follows:

To reduce our APK size and ensure that our application runs on all possible devices, we designed our application for x86, Armv7, and Arm architectures. Each style contains only local libraries corresponding to its own architecture, so it is entirely possible that someone could install apKS that were not made for their device architecture.

We started recording installation package names in crashes and quickly discovered that, yes, users were installing applications from various sources, and that every new UnsatisfiedLinkError was from a manually installed application, and that users had mistakenly installed the wrong ABI architecture for their devices. This is the final “trap,” and we are relieved that it has a very simple explanation.

introduceReLinker

We decided to package the extraction code into a small library that everyone could use. No one should go through the debugging process we went through, especially when it comes to a very basic Android feature that is outside the control of the App developer.

Using ReLinker is as simple as replacing standards

System. LoadLibrary (" mylibrary ");Copy the code

Use a callback

ReLinker. LoadLibrary (context, "mylibrary)"Copy the code

ReLinker source

conclusion

By the time the fix was released, the number of people experiencing this crash continued to reach approximately 100,000 users. We hope you will find ReLinker useful and never again encounter UnsatisfiedLinkerError randomly.