Revert "Remove old Android code that have now has alternative public APIs"

This reverts commit 03eb44394e.

Reason for revert: This breaks the dependecy update round in dev now. The revert can be reverted again after
1) Full dependency round is succeed in 'dev'
2) Android extras submodule has been removed from qt5.git#dev (7aa41d22fa485f212aebbef500ea91921c7bc38b)
3)qtmultimedia has been ported to use new api instead of this old one
4) Full dependency round with all above is succeed in 'dev'

Change-Id: I23241d2a90307074ecfc9573d2b58baba1874cfc
Reviewed-by: Assam Boudjelthia <assam.boudjelthia@qt.io>
This commit is contained in:
Jani Heikkinen 2021-06-16 02:26:14 +00:00
parent bc30d5624d
commit 046d819e2e
5 changed files with 2901 additions and 3 deletions

View File

@ -994,6 +994,7 @@ qt_internal_extend_target(Core CONDITION ANDROID AND NOT ANDROID_EMBEDDED
io/qstandardpaths_android.cpp
kernel/qcoreapplication_android.cpp
io/qstorageinfo_unix.cpp
kernel/qjni.cpp kernel/qjni_p.h
kernel/qjnienvironment.cpp kernel/qjnienvironment.h
kernel/qjniobject.cpp kernel/qjniobject.h
kernel/qjnihelpers.cpp kernel/qjnihelpers_p.h

2384
src/corelib/kernel/qjni.cpp Normal file

File diff suppressed because it is too large Load Diff

292
src/corelib/kernel/qjni_p.h Normal file
View File

@ -0,0 +1,292 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
// FIXME: Remove this once the JNI API is used by other modules.
#ifndef QJNI_P_H
#define QJNI_P_H
#include <jni.h>
#include <QtCore/private/qglobal_p.h>
#include <QtCore/qsharedpointer.h>
QT_BEGIN_NAMESPACE
struct Q_CORE_EXPORT QJNILocalRefDeleter
{
static void cleanup(jobject obj);
};
// To simplify this we only define it for jobjects.
typedef QScopedPointer<_jobject, QJNILocalRefDeleter> QJNIScopedLocalRef;
class Q_CORE_EXPORT QJNIEnvironmentPrivate
{
public:
QJNIEnvironmentPrivate();
~QJNIEnvironmentPrivate();
JNIEnv *operator->();
operator JNIEnv *() const;
static jclass findClass(const char *className, JNIEnv *env = nullptr);
private:
friend class QAndroidJniEnvironment;
Q_DISABLE_COPY_MOVE(QJNIEnvironmentPrivate)
JNIEnv *jniEnv;
};
class Q_CORE_EXPORT QJNIObjectData
{
public:
QJNIObjectData();
~QJNIObjectData();
jobject m_jobject;
jclass m_jclass;
bool m_own_jclass;
QByteArray m_className;
};
class Q_CORE_EXPORT QJNIObjectPrivate
{
public:
QJNIObjectPrivate();
explicit QJNIObjectPrivate(const char *className);
QJNIObjectPrivate(const char *className, const char *sig, ...);
explicit QJNIObjectPrivate(jclass clazz);
QJNIObjectPrivate(jclass clazz, const char *sig, ...);
// In most cases you should never call this function with a local ref. unless you intend
// to manage the local ref. yourself.
// NOTE: see fromLocalRef() for converting a local ref. to QJNIObjectPrivate.
explicit QJNIObjectPrivate(jobject globalRef);
template <typename T>
T callMethod(const char *methodName,
const char *sig,
...) const;
template <typename T>
T callMethod(const char *methodName) const;
template <typename T>
QJNIObjectPrivate callObjectMethod(const char *methodName) const;
QJNIObjectPrivate callObjectMethod(const char *methodName,
const char *sig,
...) const;
template <typename T>
static T callStaticMethod(const char *className,
const char *methodName,
const char *sig, ...);
template <typename T>
static T callStaticMethod(const char *className,
const char *methodName);
template <typename T>
static T callStaticMethod(jclass clazz,
const char *methodName,
const char *sig, ...);
template <typename T>
static T callStaticMethod(jclass clazz,
const char *methodName);
static QJNIObjectPrivate callStaticObjectMethod(const char *className,
const char *methodName,
const char *sig, ...);
static QJNIObjectPrivate callStaticObjectMethod(jclass clazz,
const char *methodName,
const char *sig, ...);
template <typename T>
T getField(const char *fieldName) const;
template <typename T>
static T getStaticField(const char *className, const char *fieldName);
template <typename T>
static T getStaticField(jclass clazz, const char *fieldName);
QJNIObjectPrivate getObjectField(const char *fieldName, const char *sig) const;
static QJNIObjectPrivate getStaticObjectField(const char *className,
const char *fieldName,
const char *sig);
static QJNIObjectPrivate getStaticObjectField(jclass clazz,
const char *fieldName,
const char *sig);
template <typename T>
void setField(const char *fieldName, T value);
template <typename T>
void setField(const char *fieldName, const char *sig, T value);
template <typename T>
static void setStaticField(const char *className,
const char *fieldName,
T value);
template <typename T>
static void setStaticField(const char *className,
const char *fieldName,
const char *sig,
T value);
template <typename T>
static void setStaticField(jclass clazz,
const char *fieldName,
const char *sig,
T value);
template <typename T>
static void setStaticField(jclass clazz,
const char *fieldName,
T value);
static QJNIObjectPrivate fromString(const QString &string);
QString toString() const;
static bool isClassAvailable(const char *className);
bool isValid() const;
jobject object() const { return d->m_jobject; }
template <typename T>
inline QJNIObjectPrivate &operator=(T o)
{
jobject jobj = static_cast<jobject>(o);
if (!isSameObject(jobj)) {
d = QSharedPointer<QJNIObjectData>::create();
if (jobj) {
QJNIEnvironmentPrivate env;
d->m_jobject = env->NewGlobalRef(jobj);
jclass objectClass = env->GetObjectClass(jobj);
d->m_jclass = static_cast<jclass>(env->NewGlobalRef(objectClass));
env->DeleteLocalRef(objectClass);
}
}
return *this;
}
// This function takes ownership of the jobject and releases the local ref. before returning.
static QJNIObjectPrivate fromLocalRef(jobject lref);
private:
friend class QAndroidJniObject;
struct QVaListPrivate { operator va_list &() const { return m_args; } va_list &m_args; };
QJNIObjectPrivate(const char *className, const char *sig, const QVaListPrivate &args);
QJNIObjectPrivate(jclass clazz, const char *sig, const QVaListPrivate &args);
template <typename T>
T callMethodV(const char *methodName,
const char *sig,
va_list args) const;
QJNIObjectPrivate callObjectMethodV(const char *methodName,
const char *sig,
va_list args) const;
template <typename T>
static T callStaticMethodV(const char *className,
const char *methodName,
const char *sig,
va_list args);
template <typename T>
static T callStaticMethodV(jclass clazz,
const char *methodName,
const char *sig,
va_list args);
static QJNIObjectPrivate callStaticObjectMethodV(const char *className,
const char *methodName,
const char *sig,
va_list args);
static QJNIObjectPrivate callStaticObjectMethodV(jclass clazz,
const char *methodName,
const char *sig,
va_list args);
bool isSameObject(jobject obj) const;
bool isSameObject(const QJNIObjectPrivate &other) const;
friend bool operator==(const QJNIObjectPrivate &, const QJNIObjectPrivate &);
friend bool operator!=(const QJNIObjectPrivate&, const QJNIObjectPrivate&);
template <typename T> friend bool operator!=(const QJNIObjectPrivate&, T);
template <typename T> friend bool operator==(const QJNIObjectPrivate&, T);
template <typename T> friend bool operator!=(T, const QJNIObjectPrivate&);
template <typename T> friend bool operator==(T, const QJNIObjectPrivate&);
QSharedPointer<QJNIObjectData> d;
};
inline bool operator==(const QJNIObjectPrivate &obj1, const QJNIObjectPrivate &obj2)
{
return obj1.isSameObject(obj2);
}
inline bool operator!=(const QJNIObjectPrivate &obj1, const QJNIObjectPrivate &obj2)
{
return !obj1.isSameObject(obj2);
}
template <typename T>
inline bool operator==(const QJNIObjectPrivate &obj1, T obj2)
{
return obj1.isSameObject(static_cast<jobject>(obj2));
}
template <typename T>
inline bool operator==(T obj1, const QJNIObjectPrivate &obj2)
{
return obj2.isSameObject(static_cast<jobject>(obj1));
}
template <typename T>
inline bool operator!=(const QJNIObjectPrivate &obj1, T obj2)
{
return !obj1.isSameObject(obj2);
}
template <typename T>
inline bool operator!=(T obj1, const QJNIObjectPrivate &obj2)
{
return !obj2.isSameObject(obj1);
}
QT_END_NAMESPACE
#endif // QJNI_P_H

View File

@ -37,14 +37,18 @@
**
****************************************************************************/
#include "qjnihelpers_p.h"
#include "qcoreapplication.h"
#include "qjnienvironment.h"
#include "qjnihelpers_p.h"
#include "qjniobject.h"
#include "qlist.h"
#include "qmutex.h"
#include "qsemaphore.h"
#include "qsharedpointer.h"
#include "qthread.h"
#include <QtCore/private/qcoreapplication_p.h>
#include <QtCore/qrunnable.h>
#include <android/log.h>
#include <deque>
@ -68,20 +72,91 @@ static JavaVM *g_javaVM = nullptr;
static jobject g_jActivity = nullptr;
static jobject g_jService = nullptr;
static jobject g_jClassLoader = nullptr;
static jclass g_jNativeClass = nullptr;
static jmethodID g_runPendingCppRunnablesMethodID = nullptr;
Q_GLOBAL_STATIC(std::deque<QtAndroidPrivate::Runnable>, g_pendingRunnables);
static QBasicMutex g_pendingRunnablesMutex;
Q_GLOBAL_STATIC_WITH_ARGS(QtAndroidPrivate::OnBindListener*, g_onBindListener, (nullptr));
Q_GLOBAL_STATIC(QMutex, g_onBindListenerMutex);
Q_GLOBAL_STATIC(QSemaphore, g_waitForServiceSetupSemaphore);
Q_GLOBAL_STATIC(QAtomicInt, g_serviceSetupLockers);
class PermissionsResultClass : public QObject
{
Q_OBJECT
public:
PermissionsResultClass(const QtAndroidPrivate::PermissionsResultFunc &func) : m_func(func) {}
Q_INVOKABLE void sendResult(const QtAndroidPrivate::PermissionsHash &result) { m_func(result); delete this;}
private:
QtAndroidPrivate::PermissionsResultFunc m_func;
};
typedef QHash<int, PermissionsResultClass*> PendingPermissionRequestsHash;
Q_GLOBAL_STATIC(PendingPermissionRequestsHash, g_pendingPermissionRequests);
static QBasicMutex g_pendingPermissionRequestsMutex;
static int nextRequestCode()
{
static QBasicAtomicInt counter = Q_BASIC_ATOMIC_INITIALIZER(0);
return counter.fetchAndAddRelaxed(1);
}
// function called from Java from Android UI thread
static void runPendingCppRunnables(JNIEnv */*env*/, jobject /*obj*/)
{
for (;;) { // run all posted runnables
QMutexLocker locker(&g_pendingRunnablesMutex);
if (g_pendingRunnables->empty()) {
break;
}
QtAndroidPrivate::Runnable runnable(std::move(g_pendingRunnables->front()));
g_pendingRunnables->pop_front();
locker.unlock();
runnable(); // run it outside the sync block!
}
}
namespace {
struct GenericMotionEventListeners {
QMutex mutex;
QList<QtAndroidPrivate::GenericMotionEventListener *> listeners;
};
enum {
PERMISSION_GRANTED = 0
};
}
Q_GLOBAL_STATIC(GenericMotionEventListeners, g_genericMotionEventListeners)
static void sendRequestPermissionsResult(JNIEnv *env, jobject /*obj*/, jint requestCode,
jobjectArray permissions, jintArray grantResults)
{
QMutexLocker locker(&g_pendingPermissionRequestsMutex);
auto it = g_pendingPermissionRequests->find(requestCode);
if (it == g_pendingPermissionRequests->end()) {
// show an error or something ?
return;
}
auto request = *it;
g_pendingPermissionRequests->erase(it);
locker.unlock();
Qt::ConnectionType connection = QThread::currentThread() == request->thread() ? Qt::DirectConnection : Qt::QueuedConnection;
QtAndroidPrivate::PermissionsHash hash;
const int size = env->GetArrayLength(permissions);
std::unique_ptr<jint[]> results(new jint[size]);
env->GetIntArrayRegion(grantResults, 0, size, results.get());
for (int i = 0 ; i < size; ++i) {
const auto &permission = QJniObject(env->GetObjectArrayElement(permissions, i)).toString();
auto value = results[i] == PERMISSION_GRANTED ?
QtAndroidPrivate::PermissionsResult::Granted :
QtAndroidPrivate::PermissionsResult::Denied;
hash[permission] = value;
}
QMetaObject::invokeMethod(request, "sendResult", connection, Q_ARG(QtAndroidPrivate::PermissionsHash, hash));
}
static jboolean dispatchGenericMotionEvent(JNIEnv *, jclass, jobject event)
{
jboolean ret = JNI_FALSE;
@ -269,12 +344,14 @@ jint QtAndroidPrivate::initJNI(JavaVM *vm, JNIEnv *env)
}
static const JNINativeMethod methods[] = {
{"runPendingCppRunnables", "()V", reinterpret_cast<void *>(runPendingCppRunnables)},
{"dispatchGenericMotionEvent", "(Landroid/view/MotionEvent;)Z", reinterpret_cast<void *>(dispatchGenericMotionEvent)},
{"dispatchKeyEvent", "(Landroid/view/KeyEvent;)Z", reinterpret_cast<void *>(dispatchKeyEvent)},
{"sendRequestPermissionsResult", "(I[Ljava/lang/String;[I)V", reinterpret_cast<void *>(sendRequestPermissionsResult)},
};
const bool regOk = (env->RegisterNatives(jQtNative, methods, sizeof(methods) / sizeof(methods[0])) == JNI_OK);
env->DeleteLocalRef(jQtNative);
if (!regOk && QJniEnvironment::checkAndClearExceptions(env))
return JNI_ERR;
@ -284,9 +361,17 @@ jint QtAndroidPrivate::initJNI(JavaVM *vm, JNIEnv *env)
if (!registerNativeInterfaceNatives())
return JNI_ERR;
g_runPendingCppRunnablesMethodID = env->GetStaticMethodID(jQtNative,
"runPendingCppRunnablesOnAndroidThread",
"()V");
g_jNativeClass = static_cast<jclass>(env->NewGlobalRef(jQtNative));
env->DeleteLocalRef(jQtNative);
qRegisterMetaType<QtAndroidPrivate::PermissionsHash>();
return JNI_OK;
}
jobject QtAndroidPrivate::activity()
{
return g_jActivity;
@ -325,6 +410,110 @@ jint QtAndroidPrivate::androidSdkVersion()
return sdkVersion;
}
void QtAndroidPrivate::runOnAndroidThread(const QtAndroidPrivate::Runnable &runnable, JNIEnv *env)
{
QMutexLocker locker(&g_pendingRunnablesMutex);
const bool triggerRun = g_pendingRunnables->empty();
g_pendingRunnables->push_back(runnable);
locker.unlock();
if (triggerRun)
env->CallStaticVoidMethod(g_jNativeClass, g_runPendingCppRunnablesMethodID);
}
static bool waitForSemaphore(int timeoutMs, QSharedPointer<QSemaphore> sem)
{
while (timeoutMs > 0) {
if (sem->tryAcquire(1, 10))
return true;
timeoutMs -= 10;
QCoreApplication::processEvents();
}
return false;
}
void QtAndroidPrivate::runOnAndroidThreadSync(const QtAndroidPrivate::Runnable &runnable, JNIEnv *env, int timeoutMs)
{
QSharedPointer<QSemaphore> sem(new QSemaphore);
runOnAndroidThread([&runnable, sem]{
runnable();
sem->release();
}, env);
waitForSemaphore(timeoutMs, sem);
}
void QtAndroidPrivate::requestPermissions(JNIEnv *env,
const QStringList &permissions,
const QtAndroidPrivate::PermissionsResultFunc &callbackFunc,
bool directCall)
{
if (androidSdkVersion() < 23 || !activity()) {
QHash<QString, QtAndroidPrivate::PermissionsResult> res;
for (const auto &perm : permissions)
res[perm] = checkPermission(perm);
callbackFunc(res);
return;
}
// Check API 23+ permissions
const int requestCode = nextRequestCode();
if (!directCall) {
QMutexLocker locker(&g_pendingPermissionRequestsMutex);
(*g_pendingPermissionRequests)[requestCode] = new PermissionsResultClass(callbackFunc);
}
runOnAndroidThread([permissions, callbackFunc, requestCode, directCall] {
if (directCall) {
QMutexLocker locker(&g_pendingPermissionRequestsMutex);
(*g_pendingPermissionRequests)[requestCode] = new PermissionsResultClass(callbackFunc);
}
QJniEnvironment env;
jclass clazz = env->FindClass("java/lang/String");
if (env.checkAndClearExceptions())
return;
auto array = env->NewObjectArray(permissions.size(), clazz, nullptr);
int index = 0;
for (const auto &perm : permissions)
env->SetObjectArrayElement(array, index++, QJniObject::fromString(perm).object());
QJniObject(activity()).callMethod<void>("requestPermissions", "([Ljava/lang/String;I)V", array, requestCode);
env->DeleteLocalRef(array);
}, env);
}
QtAndroidPrivate::PermissionsHash QtAndroidPrivate::requestPermissionsSync(JNIEnv *env, const QStringList &permissions, int timeoutMs)
{
QSharedPointer<QHash<QString, QtAndroidPrivate::PermissionsResult>> res(new QHash<QString, QtAndroidPrivate::PermissionsResult>());
QSharedPointer<QSemaphore> sem(new QSemaphore);
requestPermissions(env, permissions, [sem, res](const QHash<QString, PermissionsResult> &result){
*res = result;
sem->release();
}, true);
if (waitForSemaphore(timeoutMs, sem))
return std::move(*res);
else // mustn't touch *res
return QHash<QString, QtAndroidPrivate::PermissionsResult>();
}
QtAndroidPrivate::PermissionsResult QtAndroidPrivate::checkPermission(const QString &permission)
{
const auto res = QJniObject::callStaticMethod<jint>("org/qtproject/qt/android/QtNative",
"checkSelfPermission",
"(Ljava/lang/String;)I",
QJniObject::fromString(permission).object());
return res == PERMISSION_GRANTED ? PermissionsResult::Granted : PermissionsResult::Denied;
}
bool QtAndroidPrivate::shouldShowRequestPermissionRationale(const QString &permission)
{
if (androidSdkVersion() < 23 || !activity())
return false;
return QJniObject(activity()).callMethod<jboolean>("shouldShowRequestPermissionRationale",
"(Ljava/lang/String;)Z",
QJniObject::fromString(permission).object());
}
void QtAndroidPrivate::registerGenericMotionEventListener(QtAndroidPrivate::GenericMotionEventListener *listener)
{
QMutexLocker locker(&g_genericMotionEventListeners()->mutex);
@ -349,6 +538,13 @@ void QtAndroidPrivate::unregisterKeyEventListener(QtAndroidPrivate::KeyEventList
g_keyEventListeners()->listeners.removeOne(listener);
}
void QtAndroidPrivate::hideSplashScreen(JNIEnv *env, int duration)
{
Q_UNUSED(env)
QJniObject::callStaticMethod<void>("org/qtproject/qt/android/QtNative",
"hideSplashScreen", "(I)V", duration);
}
void QtAndroidPrivate::waitForServiceSetup()
{
g_waitForServiceSetupSemaphore->acquire();
@ -412,3 +608,5 @@ Q_CORE_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
return JNI_VERSION_1_6;
}
#include "qjnihelpers.moc"

View File

@ -54,9 +54,13 @@
#include <jni.h>
#include <functional>
#include <QtCore/private/qglobal_p.h>
#include <QHash>
#include <QMetaType>
QT_BEGIN_NAMESPACE
class QRunnable;
namespace QtAndroidPrivate
{
class Q_CORE_EXPORT ActivityResultListener
@ -102,6 +106,14 @@ namespace QtAndroidPrivate
virtual jobject onBind(jobject intent) = 0;
};
enum class PermissionsResult {
Granted,
Denied
};
typedef QHash<QString, QtAndroidPrivate::PermissionsResult> PermissionsHash;
typedef std::function<void()> Runnable;
typedef std::function<void(const PermissionsHash &)> PermissionsResultFunc;
Q_CORE_EXPORT jobject activity();
Q_CORE_EXPORT jobject service();
Q_CORE_EXPORT jobject context();
@ -110,6 +122,12 @@ namespace QtAndroidPrivate
Q_CORE_EXPORT jclass findClass(const char *className, JNIEnv *env);
jobject classLoader();
Q_CORE_EXPORT jint androidSdkVersion();
Q_CORE_EXPORT void runOnAndroidThread(const Runnable &runnable, JNIEnv *env);
Q_CORE_EXPORT void runOnAndroidThreadSync(const Runnable &runnable, JNIEnv *env, int timeoutMs = INT_MAX);
Q_CORE_EXPORT void requestPermissions(JNIEnv *env, const QStringList &permissions, const PermissionsResultFunc &callbackFunc, bool directCall = false);
Q_CORE_EXPORT PermissionsHash requestPermissionsSync(JNIEnv *env, const QStringList &permissions, int timeoutMs = INT_MAX);
Q_CORE_EXPORT PermissionsResult checkPermission(const QString &permission);
Q_CORE_EXPORT bool shouldShowRequestPermissionRationale(const QString &permission);
bool registerPermissionNatives();
bool registerNativeInterfaceNatives();
@ -133,6 +151,9 @@ namespace QtAndroidPrivate
Q_CORE_EXPORT void registerKeyEventListener(KeyEventListener *listener);
Q_CORE_EXPORT void unregisterKeyEventListener(KeyEventListener *listener);
// TODO: Remove once other modules refectoring is done and androidextras is not needed.
Q_CORE_EXPORT void hideSplashScreen(JNIEnv *env, int duration = 0);
Q_CORE_EXPORT void waitForServiceSetup();
Q_CORE_EXPORT int acuqireServiceSetup(int flags);
Q_CORE_EXPORT void setOnBindListener(OnBindListener *listener);
@ -180,4 +201,6 @@ namespace QtAndroidPrivate
QT_END_NAMESPACE
Q_DECLARE_METATYPE(QtAndroidPrivate::PermissionsHash)
#endif // QJNIHELPERS_H