#java #android #java-native-interface
#java #Android #java-native-интерфейс
Вопрос:
Я получил библиотеку Android .so от клиента, и я должен интегрировать ее в свой проект Xamarin Forms. Библиотека помогает приложению подключаться к устройству Интернета вещей. Поскольку методы библиотеки имеют следующую подпись, я решил написать java-оболочку для упрощения параметров и создания файла aar. После этого я изначально привязываю aar и использую его как dll в своем проекте.
Важно отметить, что проблема в Xamarin возникает только тогда, когда цель компиляции> 10. В противном случае все работает нормально. Я предполагаю, что последние обновления интерфейсов, отличных от SDK, сломали приложение.
Заголовок библиотеки:
public static native int ReadParams(String token, StringBuilder serial, StringBuilder ssid, StringBuilder password, StringBuilder sensor, Integer keepAlive);
Проблема:
Метод отлично работает при вызове из собственного приложения Android, однако вылетает со следующей ошибкой из Xamarin Forms. Сбой происходит в следующей строке в оболочке Java.
Аварийная строка:
StringBuilder strSerial = new StringBuilder();
StringBuilder strssid = new StringBuilder();
StringBuilder strpassword = new StringBuilder();
StringBuilder strsensor = new StringBuilder();
Integer keepAlive = new Integer(0);
//Crash on below line
int response = EPM002Lib.ReadParams(token, strSerial, strssid, strpassword, strsensor, keepAlive);
Трассировка стека:
— Конец управляемого Java.Lang.Несовместимостьclasschangeerror трассировка стека — java.lang.NoSuchFieldError: нет поля «I» «значение» в классе «Ljava / lang / Integer;» или его суперклассов в com.esong.lib.EPM002Lib.ReadParams(собственный метод) в com.sensorwa.config.configdemo.SquareSdkhelper.ReadParams(SquareSdkhelper.java:32)
Я понимаю, что дополнительная информация о внутренней функциональности параметров EPM002Lib.ReadParams помогла бы, однако библиотека, похоже, работает с собственным приложением для Android (даже при компиляции с Android 10). Пожалуйста, не стесняйтесь запрашивать дополнительную информацию или предлагать предложения. Спасибо за помощь 😄
Комментарии:
1. Зачем библиотеке вообще нужен доступ к
int
полю? Это звучит сломано.Integer
предполагается, что значения должны быть неизменяемыми, поэтому библиотека не должна пытаться изменить значение. И если они просто хотели прочитать значение, они должны использоватьintValue
метод.2. Да, я понимаю. Тем не менее, мне просто любопытно, как это работает на родном Android :/
3. Ну, не совсем ясно, что вы подразумеваете под «скомпилированным для Android 10» . Если вы имеете в
compileSdkVersion
виду, то это здесь не совсем уместно. ВажнаtargetSdkVersion
(и, конечно, фактическая версия Android, на которой вы запускаете приложение).4. Да, это действительно compileSdkVersion, и я думаю, что это актуально, поэтому изменения в интерфейсах, отличных от sdk, были внесены в sdk 10. Насколько я понимаю, targetSdkVersion — это версия, на которую нацелено приложение, возможно, с большинством пользователей. база.
5. Похоже, вы неправильно поняли роль
compileSdkVersion
; он определяет только, какие API доступны вам во время компиляции. Это тоtargetSdkVersion
, что имеет значение, когда дело доходит до определения того, как применяются такие изменения поведения во время выполнения. На странице, на которую вы ссылались в своем вопросе, даже написано так: «эти интерфейсы принадлежат спискуmax-target-p
(greylist-max-p
), поэтому ваше приложение может использовать эти интерфейсы, только если оно нацелено на Android 9 (уровень API 28) или ниже» .
Ответ №1:
Я скомпилировал этот файл с помощью компилятора Android Aarch64:
#include <jni.h>
int access_field(JNIEnv *env, jobject obj) {
jclass cls_Integer = (*env)->FindClass(env, "Ljava/lang/Integer;");
jfieldID fid_Integer_value = (*env)->GetFieldID(env, cls_Integer, "value", "I");
return (*env)->GetIntField(env, obj, fid_Integer_value);
}
int access_method(JNIEnv *env, jobject obj) {
jclass cls_Integer = (*env)->FindClass(env, "Ljava/lang/Integer;");
jmethodID mid_Integer_value = (*env)->GetMethodID(env, cls_Integer, "intValue", "()I");
return (*env)->CallIntMethod(env, obj, mid_Integer_value);
}
что приводит к следующему коду для access_field
:
int access_field(JNIEnv *env, jobject obj) {
0: d10103ff sub sp, sp, #0x40
4: a9037bfd stp x29, x30, [sp,#48]
8: 9100c3fd add x29, sp, #0x30
c: 90000008 adrp x8, 0 <access_field>
10: 91000108 add x8, x8, #0x0
14: 90000002 adrp x2, 0 <access_field>
18: 91000042 add x2, x2, #0x0
1c: 90000003 adrp x3, 0 <access_field>
20: 91000063 add x3, x3, #0x0
24: f81f83a0 stur x0, [x29,#-8]
28: f81f03a1 stur x1, [x29,#-16]
jclass cls_Integer = (*env)->FindClass(env, "Ljava/lang/Integer;");
2c: f85f83a9 ldur x9, [x29,#-8]
30: f9400129 ldr x9, [x9]
34: f9401929 ldr x9, [x9,#48]
38: f85f83a0 ldur x0, [x29,#-8]
3c: aa0803e1 mov x1, x8
40: f90007e2 str x2, [sp,#8]
44: f90003e3 str x3, [sp]
48: d63f0120 blr x9
4c: f9000fe0 str x0, [sp,#24]
jfieldID fid_Integer_value = (*env)->GetFieldID(env, cls_Integer, "value", "I");
50: f85f83a8 ldur x8, [x29,#-8]
54: f9400108 ldr x8, [x8]
58: f9417908 ldr x8, [x8,#752]
5c: f85f83a0 ldur x0, [x29,#-8]
60: f9400fe1 ldr x1, [sp,#24]
64: f94007e2 ldr x2, [sp,#8]
68: f94003e3 ldr x3, [sp]
6c: d63f0100 blr x8
70: f9000be0 str x0, [sp,#16]
return (*env)->GetIntField(env, obj, fid_Integer_value);
74: f85f83a8 ldur x8, [x29,#-8]
78: f9400108 ldr x8, [x8]
7c: f9419108 ldr x8, [x8,#800]
80: f85f83a0 ldur x0, [x29,#-8]
84: f85f03a1 ldur x1, [x29,#-16]
88: f9400be2 ldr x2, [sp,#16]
8c: d63f0100 blr x8
90: a9437bfd ldp x29, x30, [sp,#48]
94: 910103ff add sp, sp, #0x40
98: d65f03c0 ret
}
и для access_method
:
int access_method(JNIEnv *env, jobject obj) {
9c: d10103ff sub sp, sp, #0x40
a0: a9037bfd stp x29, x30, [sp,#48]
a4: 9100c3fd add x29, sp, #0x30
a8: 90000008 adrp x8, 0 <access_field>
ac: 91000108 add x8, x8, #0x0
b0: 90000002 adrp x2, 0 <access_field>
b4: 91000042 add x2, x2, #0x0
b8: 90000003 adrp x3, 0 <access_field>
bc: 91000063 add x3, x3, #0x0
c0: f81f83a0 stur x0, [x29,#-8]
c4: f81f03a1 stur x1, [x29,#-16]
jclass cls_Integer = (*env)->FindClass(env, "Ljava/lang/Integer;");
c8: f85f83a9 ldur x9, [x29,#-8]
cc: f9400129 ldr x9, [x9]
d0: f9401929 ldr x9, [x9,#48]
d4: f85f83a0 ldur x0, [x29,#-8]
d8: aa0803e1 mov x1, x8
dc: f90007e2 str x2, [sp,#8]
e0: f90003e3 str x3, [sp]
e4: d63f0120 blr x9
e8: f9000fe0 str x0, [sp,#24]
jmethodID mid_Integer_value = (*env)->GetMethodID(env, cls_Integer, "intValue", "()I");
ec: f85f83a8 ldur x8, [x29,#-8]
f0: f9400108 ldr x8, [x8]
f4: f9408508 ldr x8, [x8,#264]
f8: f85f83a0 ldur x0, [x29,#-8]
fc: f9400fe1 ldr x1, [sp,#24]
100: f94007e2 ldr x2, [sp,#8]
104: f94003e3 ldr x3, [sp]
108: d63f0100 blr x8
10c: f9000be0 str x0, [sp,#16]
return (*env)->CallIntMethod(env, obj, mid_Integer_value);
110: f85f83a8 ldur x8, [x29,#-8]
114: f9400108 ldr x8, [x8]
118: f940c508 ldr x8, [x8,#392]
11c: f85f83a0 ldur x0, [x29,#-8]
120: f85f03a1 ldur x1, [x29,#-16]
124: f9400be2 ldr x2, [sp,#16]
128: d63f0100 blr x8
12c: a9437bfd ldp x29, x30, [sp,#48]
130: 910103ff add sp, sp, #0x40
134: d65f03c0 ret
Основными отличиями являются смещения, используемые в ldr x8
вызовах. Это смещения в таблицу указателей функций внутри JNIEnv
, более конкретно:
GetFieldID
имеет смещение 752GetIntField
имеет смещение 800GetMethodID
имеет смещение 264CallIntMethod
имеет смещение 392.
Другим отличием является подпись, переданная в GetIntField
or GetMethodID
, которая вводится во время компоновки. Объектный файл, который я сбросил, еще не связан, поэтому там есть фиктивные инструкции. Это четвертый аргумент, поэтому он передается в регистре x3
.
Итак, подводя итог, вам нужно сделать следующее:
- Найдите адрес строки
"()I"
где-нибудь в библиотеке или добавьте его в таблицу строк. - Вам нужно найти все места, к которым
java.lang.Integer#value
осуществляется доступ. - Замените два смещения указателя функции (752 -> 264; 800 -> 392) в
ldr x8
правом углу передblr x8
звонком. - Найдите код, который изменяется
x3
, и вместо этого укажите на"()I"
него.
Удачи!
Комментарии:
1. Большое вам спасибо, что нашли время ответить. Я постараюсь реализовать это через неделю 🙂
Ответ №2:
Вы полагаетесь на детали реализации, на которые нельзя было полагаться.
Теперь он сломался, и вы можете сохранить обе части.
Комментарии:
1. Волшебной палочки нет, вам придется изменить эту библиотеку, как указано на вашем связанном сайте. Google предупреждал об этом годами.