2019-12-31 18:19:35 +01:00
/ *
* Copyright ( C ) 2012 The Android Open Source Project
2023-10-17 13:44:01 +02:00
* modified
* SPDX - License - Identifier : Apache - 2 . 0 AND GPL - 3 . 0 - only
2019-12-31 18:19:35 +01:00
* /
2024-01-31 18:32:43 +01:00
package helium314.keyboard.latin.utils ;
2019-12-31 18:19:35 +01:00
2024-01-01 20:35:12 +01:00
import android.annotation.SuppressLint ;
2023-06-28 09:27:07 +02:00
import android.app.Application ;
2024-01-01 20:35:12 +01:00
import android.os.Build ;
import android.text.TextUtils ;
2019-12-31 18:19:35 +01:00
2024-01-28 18:05:38 +01:00
import androidx.preference.PreferenceManager ;
2024-01-31 18:32:43 +01:00
import helium314.keyboard.latin.App ;
import helium314.keyboard.latin.BuildConfig ;
import helium314.keyboard.latin.settings.Settings ;
2024-01-28 18:05:38 +01:00
2023-06-28 09:27:07 +02:00
import java.io.File ;
2024-01-01 20:35:12 +01:00
import java.io.FileInputStream ;
2019-12-31 18:19:35 +01:00
public final class JniUtils {
private static final String TAG = JniUtils . class . getSimpleName ( ) ;
2024-01-01 19:20:28 +01:00
public static final String JNI_LIB_NAME = " jni_latinime " ;
public static final String JNI_LIB_NAME_GOOGLE = " jni_latinimegoogle " ;
public static final String JNI_LIB_IMPORT_FILE_NAME = " libjni_latinime.so " ;
2024-01-01 20:35:12 +01:00
private static final String CHECKSUM_ARM64 = " b1049983e6ac5cfc6d1c66e38959751044fad213dff0637a6cf1d2a2703e754f " ;
private static final String CHECKSUM_ARM32 = " 442a2a8bfcb25489564bc9433a916fa4dc0dba9000fe6f6f03f5939b985091e6 " ;
private static final String CHECKSUM_X86_64 = " c882e12e6d48dd946e0b644c66868a720bd11ac3fecf152000e21a3d5abd59c9 " ;
private static final String CHECKSUM_X86 = " bd946d126c957b5a6dea3bafa07fa36a27950b30e2b684dffc60746d0a1c7ad8 " ;
2024-01-01 19:20:28 +01:00
2024-01-01 20:35:12 +01:00
public static String expectedDefaultChecksum ( ) {
2024-01-18 16:27:22 +01:00
final String abi = Build . SUPPORTED_ABIS [ 0 ] ;
2024-01-01 20:35:12 +01:00
return switch ( abi ) {
case " arm64-v8a " - > CHECKSUM_ARM64 ;
case " armeabi-v7a " - > CHECKSUM_ARM32 ;
case " x86_64 " - > CHECKSUM_X86_64 ;
case " x86 " - > CHECKSUM_X86 ;
default - > " - " ; // invalid checksum that definitely will not match
} ;
}
2019-12-31 18:19:35 +01:00
public static boolean sHaveGestureLib = false ;
static {
2024-01-01 20:35:12 +01:00
// hardcoded default path, may not work on all phones
@SuppressLint ( " SdCardPath " ) String filesDir = " /data/data/ " + BuildConfig . APPLICATION_ID + " /files " ;
2024-01-28 18:05:38 +01:00
Application app = App . Companion . getApp ( ) ;
if ( app = = null ) {
try {
// try using reflection to get (app)context: https://stackoverflow.com/a/38967293
// this may not be necessary any more, now that we get the app somewhere else?
app = ( Application ) Class . forName ( " android.app.ActivityThread " )
. getMethod ( " currentApplication " ) . invoke ( null , ( Object [ ] ) null ) ;
} catch ( Exception ignored ) { }
}
if ( app ! = null ) // use the actual path if possible
2023-11-04 14:01:56 +01:00
filesDir = app . getFilesDir ( ) . getAbsolutePath ( ) ;
2024-01-01 19:20:28 +01:00
final File userSuppliedLibrary = new File ( filesDir + File . separator + JNI_LIB_IMPORT_FILE_NAME ) ;
2024-02-01 22:10:54 +01:00
if ( ! BuildConfig . BUILD_TYPE . equals ( " nouserlib " ) & & userSuppliedLibrary . exists ( ) ) {
2024-01-28 18:05:38 +01:00
String wantedChecksum = expectedDefaultChecksum ( ) ;
2023-06-28 09:27:07 +02:00
try {
2024-01-28 18:05:38 +01:00
if ( app ! = null ) {
// we want the default preferences, because storing the checksum in device protected storage is discouraged
// see https://developer.android.com/reference/android/content/Context#createDeviceProtectedStorageContext()
2024-02-13 08:23:07 +01:00
// if device is locked, this will throw an IllegalStateException
2024-01-28 18:05:38 +01:00
wantedChecksum = PreferenceManager . getDefaultSharedPreferences ( app ) . getString ( Settings . PREF_LIBRARY_CHECKSUM , wantedChecksum ) ;
}
2024-01-01 20:35:12 +01:00
final String checksum = ChecksumCalculator . INSTANCE . checksum ( new FileInputStream ( userSuppliedLibrary ) ) ;
if ( TextUtils . equals ( wantedChecksum , checksum ) ) {
// try loading the library
System . load ( userSuppliedLibrary . getAbsolutePath ( ) ) ;
sHaveGestureLib = true ; // this is an assumption, any way to actually check?
} else {
// delete if checksum doesn't match
2024-01-28 18:05:38 +01:00
// this is bad if we can't get the application and the user has a different library than expected...
userSuppliedLibrary . delete ( ) ;
sHaveGestureLib = false ;
2024-01-01 20:35:12 +01:00
}
2023-11-04 14:01:56 +01:00
} catch ( Throwable t ) { // catch everything, maybe provided library simply doesn't work
2024-02-13 08:23:07 +01:00
if ( ! ( t instanceof IllegalStateException ) | | ! " SharedPreferences in credential encrypted storage are not available until after user is unlocked " . equals ( t . getMessage ( ) ) )
// but don't log if device is locked, here we expect the exception and only load system library, if possible
Log . w ( TAG , " Could not load user-supplied library " , t ) ;
2023-06-28 09:27:07 +02:00
}
2023-11-04 14:01:56 +01:00
}
if ( ! sHaveGestureLib ) {
2024-01-01 20:35:12 +01:00
// try loading google library, will fail unless the library is in system and this is a system app
2023-06-28 09:27:07 +02:00
try {
2024-01-01 19:20:28 +01:00
System . loadLibrary ( JNI_LIB_NAME_GOOGLE ) ;
2023-06-28 09:27:07 +02:00
sHaveGestureLib = true ;
} catch ( UnsatisfiedLinkError ul ) {
2024-01-01 19:20:28 +01:00
Log . w ( TAG , " Could not load system glide typing library " + JNI_LIB_NAME_GOOGLE , ul ) ;
2023-11-04 14:01:56 +01:00
}
}
if ( ! sHaveGestureLib ) {
// try loading built-in library
try {
2024-01-01 19:20:28 +01:00
System . loadLibrary ( JNI_LIB_NAME ) ;
2023-11-04 14:01:56 +01:00
} catch ( UnsatisfiedLinkError ul ) {
2024-01-01 19:20:28 +01:00
Log . w ( TAG , " Could not load native library " + JNI_LIB_NAME , ul ) ;
2023-06-28 09:27:07 +02:00
}
2019-12-31 18:19:35 +01:00
}
}
private JniUtils ( ) {
// This utility class is not publicly instantiable.
}
public static void loadNativeLibrary ( ) {
// Ensures the static initializer is called
}
}