Android: clean up error handling and native methods registration

Emit log output and return false immediately if we fail to get both the
Activity and the Service objects. Standardize the registerNative methods
to return bool and take a QJniEnvironment, and adjust the macros
accordingly.

Simplify the startup routine to use QJniEnvironment.

Change-Id: I11be35426520dc803f5a07bbb495e908592f254e
Reviewed-by: Tinja Paavoseppä <tinja.paavoseppa@qt.io>
This commit is contained in:
Volker Hilsheimer 2023-10-27 11:25:02 +02:00
parent 6735aa868f
commit daf15a4aaf
9 changed files with 72 additions and 88 deletions

View File

@ -21,7 +21,6 @@
#include <QtCore/qvarlengtharray.h>
static const char m_qtTag[] = "Qt A11Y";
static const char m_classErrorMsg[] = "Can't find class \"%s\"";
QT_BEGIN_NAMESPACE
@ -353,16 +352,6 @@ namespace QtAndroidAccessibility
return result && oldPosition != screenRect_helper(firstChildId, false);
}
#define FIND_AND_CHECK_CLASS(CLASS_NAME) \
clazz = env->FindClass(CLASS_NAME); \
if (!clazz) { \
__android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_classErrorMsg, CLASS_NAME); \
return JNI_FALSE; \
}
//__android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_methodErrorMsg, METHOD_NAME, METHOD_SIGNATURE);
static QString textFromValue(QAccessibleInterface *iface)
{
QString valueStr;
@ -559,7 +548,7 @@ if (!clazz) { \
return true;
}
static JNINativeMethod methods[] = {
static const JNINativeMethod methods[] = {
{"setActive","(Z)V",(void*)setActive},
{"childIdListForAccessibleObject", "(I)[I", (jintArray)childIdListForAccessibleObject},
{"parentId", "(I)I", (void*)parentId},
@ -579,13 +568,10 @@ if (!clazz) { \
return false; \
}
bool registerNatives(JNIEnv *env)
bool registerNatives(QJniEnvironment &env)
{
jclass clazz;
FIND_AND_CHECK_CLASS("org/qtproject/qt/android/accessibility/QtNativeAccessibility");
jclass appClass = static_cast<jclass>(env->NewGlobalRef(clazz));
if (env->RegisterNatives(appClass, methods, sizeof(methods) / sizeof(methods[0])) < 0) {
if (!env.registerNativeMethods("org/qtproject/qt/android/accessibility/QtNativeAccessibility",
methods, sizeof(methods) / sizeof(methods[0]))) {
__android_log_print(ANDROID_LOG_FATAL,"Qt A11y", "RegisterNatives failed");
return false;
}

View File

@ -9,12 +9,13 @@
QT_BEGIN_NAMESPACE
class QObject;
class QJniEnvironment;
namespace QtAndroidAccessibility
{
void initialize();
bool isActive();
bool registerNatives(JNIEnv *env);
bool registerNatives(QJniEnvironment &env);
void notifyLocationChange(uint accessibilityObjectId);
void notifyObjectHide(uint accessibilityObjectId);
void notifyObjectFocus(uint accessibilityObjectId);

View File

@ -921,11 +921,10 @@ namespace QtAndroidInput
{"dispatchKeyEvent", "(Landroid/view/KeyEvent;)Z", reinterpret_cast<void *>(dispatchKeyEvent)},
};
bool registerNatives()
bool registerNatives(QJniEnvironment &env)
{
QJniEnvironment qenv;
if (!qenv.registerNativeMethods(QtJniTypes::Traits<QtJniTypes::QtInputDelegate>::className(),
methods, sizeof(methods) / sizeof(methods[0]))) {
if (!env.registerNativeMethods(QtJniTypes::Traits<QtJniTypes::QtInputDelegate>::className(),
methods, sizeof(methods) / sizeof(methods[0]))) {
__android_log_print(ANDROID_LOG_FATAL,"Qt", "RegisterNatives failed");
return false;
}

View File

@ -13,6 +13,8 @@ QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcQpaInputMethods);
class QJniEnvironment;
namespace QtAndroidInput
{
// Software keyboard support
@ -49,7 +51,7 @@ namespace QtAndroidInput
void registerKeyEventListener(KeyEventListener *listener);
void unregisterKeyEventListener(KeyEventListener *listener);
bool registerNatives();
bool registerNatives(QJniEnvironment &env);
}
QT_END_NAMESPACE

View File

@ -29,6 +29,7 @@
#include <QtCore/qjniobject.h>
#include <QtCore/qprocess.h>
#include <QtCore/qresource.h>
#include <QtCore/qscopeguard.h>
#include <QtCore/qthread.h>
#include <QtGui/private/qguiapplication_p.h>
#include <QtGui/private/qhighdpiscaling_p.h>
@ -807,46 +808,47 @@ static JNINativeMethod methods[] = {
clazz = env->FindClass(CLASS_NAME); \
if (!clazz) { \
__android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_classErrorMsg, CLASS_NAME); \
return JNI_FALSE; \
return false; \
}
#define GET_AND_CHECK_METHOD(VAR, CLASS, METHOD_NAME, METHOD_SIGNATURE) \
VAR = env->GetMethodID(CLASS, METHOD_NAME, METHOD_SIGNATURE); \
if (!VAR) { \
__android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_methodErrorMsg, METHOD_NAME, METHOD_SIGNATURE); \
return JNI_FALSE; \
return false; \
}
#define GET_AND_CHECK_STATIC_METHOD(VAR, CLASS, METHOD_NAME, METHOD_SIGNATURE) \
VAR = env->GetStaticMethodID(CLASS, METHOD_NAME, METHOD_SIGNATURE); \
if (!VAR) { \
__android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_methodErrorMsg, METHOD_NAME, METHOD_SIGNATURE); \
return JNI_FALSE; \
return false; \
}
#define GET_AND_CHECK_FIELD(VAR, CLASS, FIELD_NAME, FIELD_SIGNATURE) \
VAR = env->GetFieldID(CLASS, FIELD_NAME, FIELD_SIGNATURE); \
if (!VAR) { \
__android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_methodErrorMsg, FIELD_NAME, FIELD_SIGNATURE); \
return JNI_FALSE; \
return false; \
}
#define GET_AND_CHECK_STATIC_FIELD(VAR, CLASS, FIELD_NAME, FIELD_SIGNATURE) \
VAR = env->GetStaticFieldID(CLASS, FIELD_NAME, FIELD_SIGNATURE); \
if (!VAR) { \
__android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_methodErrorMsg, FIELD_NAME, FIELD_SIGNATURE); \
return JNI_FALSE; \
return false; \
}
static int registerNatives(JNIEnv *env)
static bool registerNatives(QJniEnvironment &env)
{
jclass clazz;
FIND_AND_CHECK_CLASS("org/qtproject/qt/android/QtNative");
m_applicationClass = static_cast<jclass>(env->NewGlobalRef(clazz));
if (env->RegisterNatives(m_applicationClass, methods, sizeof(methods) / sizeof(methods[0])) < 0) {
if (!env.registerNativeMethods(m_applicationClass,
methods, sizeof(methods) / sizeof(methods[0]))) {
__android_log_print(ANDROID_LOG_FATAL,"Qt", "RegisterNatives failed");
return JNI_FALSE;
return false;
}
GET_AND_CHECK_STATIC_METHOD(m_createSurfaceMethodID, m_applicationClass, "createSurface", "(IZIIIII)V");
@ -860,42 +862,46 @@ static int registerNatives(JNIEnv *env)
GET_AND_CHECK_STATIC_METHOD(methodID, m_applicationClass, "service", "()Landroid/app/Service;");
contextObject = env->CallStaticObjectMethod(m_applicationClass, methodID);
}
if (!contextObject) {
__android_log_print(ANDROID_LOG_FATAL,"Qt", "Failed to get Activity or Service object");
return false;
}
const auto releaseContextObject = qScopeGuard([&env, contextObject]{
env->DeleteLocalRef(contextObject);
});
GET_AND_CHECK_STATIC_METHOD(methodID, m_applicationClass, "classLoader", "()Ljava/lang/ClassLoader;");
m_classLoaderObject = env->NewGlobalRef(env->CallStaticObjectMethod(m_applicationClass, methodID));
clazz = env->GetObjectClass(m_classLoaderObject);
GET_AND_CHECK_METHOD(m_loadClassMethodID, clazz, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
if (contextObject) {
FIND_AND_CHECK_CLASS("android/content/ContextWrapper");
GET_AND_CHECK_METHOD(methodID, clazz, "getAssets", "()Landroid/content/res/AssetManager;");
m_assets = env->NewGlobalRef(env->CallObjectMethod(contextObject, methodID));
m_assetManager = AAssetManager_fromJava(env, m_assets);
FIND_AND_CHECK_CLASS("android/content/ContextWrapper");
GET_AND_CHECK_METHOD(methodID, clazz, "getAssets", "()Landroid/content/res/AssetManager;");
m_assets = env->NewGlobalRef(env->CallObjectMethod(contextObject, methodID));
m_assetManager = AAssetManager_fromJava(env.jniEnv(), m_assets);
GET_AND_CHECK_METHOD(methodID, clazz, "getResources", "()Landroid/content/res/Resources;");
m_resourcesObj = env->NewGlobalRef(env->CallObjectMethod(contextObject, methodID));
GET_AND_CHECK_METHOD(methodID, clazz, "getResources", "()Landroid/content/res/Resources;");
m_resourcesObj = env->NewGlobalRef(env->CallObjectMethod(contextObject, methodID));
FIND_AND_CHECK_CLASS("android/graphics/Bitmap");
m_bitmapClass = static_cast<jclass>(env->NewGlobalRef(clazz));
GET_AND_CHECK_STATIC_METHOD(m_createBitmapMethodID, m_bitmapClass
, "createBitmap", "(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;");
FIND_AND_CHECK_CLASS("android/graphics/Bitmap$Config");
jfieldID fieldId;
GET_AND_CHECK_STATIC_FIELD(fieldId, clazz, "ARGB_8888", "Landroid/graphics/Bitmap$Config;");
m_ARGB_8888_BitmapConfigValue = env->NewGlobalRef(env->GetStaticObjectField(clazz, fieldId));
GET_AND_CHECK_STATIC_FIELD(fieldId, clazz, "RGB_565", "Landroid/graphics/Bitmap$Config;");
m_RGB_565_BitmapConfigValue = env->NewGlobalRef(env->GetStaticObjectField(clazz, fieldId));
FIND_AND_CHECK_CLASS("android/graphics/Bitmap");
m_bitmapClass = static_cast<jclass>(env->NewGlobalRef(clazz));
GET_AND_CHECK_STATIC_METHOD(m_createBitmapMethodID, m_bitmapClass,
"createBitmap", "(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;");
FIND_AND_CHECK_CLASS("android/graphics/Bitmap$Config");
jfieldID fieldId;
GET_AND_CHECK_STATIC_FIELD(fieldId, clazz, "ARGB_8888", "Landroid/graphics/Bitmap$Config;");
m_ARGB_8888_BitmapConfigValue = env->NewGlobalRef(env->GetStaticObjectField(clazz, fieldId));
GET_AND_CHECK_STATIC_FIELD(fieldId, clazz, "RGB_565", "Landroid/graphics/Bitmap$Config;");
m_RGB_565_BitmapConfigValue = env->NewGlobalRef(env->GetStaticObjectField(clazz, fieldId));
FIND_AND_CHECK_CLASS("android/graphics/drawable/BitmapDrawable");
m_bitmapDrawableClass = static_cast<jclass>(env->NewGlobalRef(clazz));
GET_AND_CHECK_METHOD(m_bitmapDrawableConstructorMethodID,
m_bitmapDrawableClass,
"<init>",
"(Landroid/content/res/Resources;Landroid/graphics/Bitmap;)V");
FIND_AND_CHECK_CLASS("android/graphics/drawable/BitmapDrawable");
m_bitmapDrawableClass = static_cast<jclass>(env->NewGlobalRef(clazz));
GET_AND_CHECK_METHOD(m_bitmapDrawableConstructorMethodID,
m_bitmapDrawableClass,
"<init>", "(Landroid/content/res/Resources;Landroid/graphics/Bitmap;)V");
env->DeleteLocalRef(contextObject);
}
return JNI_TRUE;
return true;
}
QT_END_NAMESPACE
@ -908,23 +914,16 @@ Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void */*reserved*/)
initialized = true;
QT_USE_NAMESPACE
typedef union {
JNIEnv *nativeEnvironment;
void *venv;
} UnionJNIEnvToVoid;
UnionJNIEnvToVoid uenv;
uenv.venv = nullptr;
m_javaVM = nullptr;
if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_6) != JNI_OK) {
__android_log_print(ANDROID_LOG_FATAL, "Qt", "GetEnv failed");
m_javaVM = vm;
QJniEnvironment env;
if (!env.isValid()) {
m_javaVM = nullptr;
__android_log_print(ANDROID_LOG_FATAL, "Qt", "Failed to initialize the JNI Environment");
return -1;
}
JNIEnv *env = uenv.nativeEnvironment;
if (!registerNatives(env)
|| !QtAndroidInput::registerNatives()
|| !QtAndroidInput::registerNatives(env)
|| !QtAndroidMenu::registerNatives(env)
|| !QtAndroidAccessibility::registerNatives(env)
|| !QtAndroidDialogHelpers::registerNatives(env)
@ -934,11 +933,8 @@ Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void */*reserved*/)
}
QWindowSystemInterfacePrivate::TabletEvent::setPlatformSynthesizesMouse(false);
m_javaVM = vm;
// attach qt main thread data to this thread
QObject threadSetter;
if (threadSetter.thread())
threadSetter.thread()->setObjectName("QtMainLoopThread");
QThread::currentThread()->setObjectName("QtMainLoopThread");
__android_log_print(ANDROID_LOG_INFO, "Qt", "qt started");
return JNI_VERSION_1_6;
}

View File

@ -378,11 +378,11 @@ namespace QtAndroidMenu
return false; \
}
bool registerNatives(JNIEnv *env)
bool registerNatives(QJniEnvironment &env)
{
jclass appClass = applicationClass();
if (env->RegisterNatives(appClass, methods, sizeof(methods) / sizeof(methods[0])) < 0) {
if (!env.registerNativeMethods(appClass, methods, sizeof(methods) / sizeof(methods[0]))) {
__android_log_print(ANDROID_LOG_FATAL,"Qt", "RegisterNatives failed");
return false;
}

View File

@ -15,6 +15,7 @@ class QAndroidPlatformMenuItem;
class QWindow;
class QRect;
class QPoint;
class QJniEnvironment;
namespace QtAndroidMenu
{
@ -31,7 +32,7 @@ namespace QtAndroidMenu
void removeMenuBar(QAndroidPlatformMenuBar *menuBar);
// Menu support
bool registerNatives(JNIEnv *env);
bool registerNatives(QJniEnvironment &env);
}
QT_END_NAMESPACE

View File

@ -150,7 +150,7 @@ static void dialogResult(JNIEnv * /*env*/, jobject /*thiz*/, jlong handler, int
QMetaObject::invokeMethod(object, "dialogResult", Qt::QueuedConnection, Q_ARG(int, buttonID));
}
static JNINativeMethod methods[] = {
static const JNINativeMethod methods[] = {
{"dialogResult", "(JI)V", (void *)dialogResult}
};
@ -162,21 +162,19 @@ static JNINativeMethod methods[] = {
return false; \
}
bool registerNatives(JNIEnv *env)
bool registerNatives(QJniEnvironment &env)
{
const char QtMessageHandlerHelperClassName[] = "org/qtproject/qt/android/QtMessageDialogHelper";
QJniEnvironment qenv;
jclass clazz = qenv.findClass(QtMessageHandlerHelperClassName);
jclass clazz = env.findClass(QtMessageHandlerHelperClassName);
if (!clazz) {
__android_log_print(ANDROID_LOG_FATAL, QtAndroid::qtTagText(), QtAndroid::classErrorMsgFmt()
, QtMessageHandlerHelperClassName);
return false;
}
g_messageDialogHelperClass = static_cast<jclass>(env->NewGlobalRef(clazz));
FIND_AND_CHECK_CLASS("org/qtproject/qt/android/QtNativeDialogHelper");
jclass appClass = static_cast<jclass>(env->NewGlobalRef(clazz));
if (env->RegisterNatives(appClass, methods, sizeof(methods) / sizeof(methods[0])) < 0) {
if (!env.registerNativeMethods("org/qtproject/qt/android/QtNativeDialogHelper",
methods, sizeof(methods) / sizeof(methods[0]))) {
__android_log_print(ANDROID_LOG_FATAL, "Qt", "RegisterNatives failed");
return false;
}

View File

@ -8,12 +8,13 @@
#include <jni.h>
#include <QEventLoop>
#include <QtCore/QJniEnvironment>
#include <QtCore/QJniObject>
#include <qpa/qplatformdialoghelper.h>
QT_BEGIN_NAMESPACE
class QJniEnvironment;
namespace QtAndroidDialogHelpers {
class QAndroidPlatformMessageDialogHelper: public QPlatformMessageDialogHelper
@ -41,7 +42,7 @@ private:
};
bool registerNatives(JNIEnv *env);
bool registerNatives(QJniEnvironment &env);
}