Android: Make sure we check and clear exceptions from the camera.

In several places we where ignoring the fact that we might get an
exception from the camera code on Android. Failing to clear them will
cause the application to to terminate.

Task-number: QTBUG-39425
Change-Id: Idfe40e1749f54d551d37dae25912d9ddbc3da01e
Reviewed-by: Yoann Lopes <yoann.lopes@digia.com>
This commit is contained in:
Christian Strømme
2014-07-10 16:56:23 +02:00
committed by Christian Stromme
parent 389d66b3ed
commit a7d10a265a
3 changed files with 59 additions and 36 deletions

View File

@@ -331,11 +331,12 @@ bool QAndroidCameraSession::startPreview()
if (m_previewStarted)
return true;
if (m_videoOutput->isReady())
m_camera->setPreviewTexture(m_videoOutput->surfaceTexture());
else
if (!m_videoOutput->isReady())
return true; // delay starting until the video output is ready
if (!m_camera->setPreviewTexture(m_videoOutput->surfaceTexture()))
return false;
m_status = QCamera::StartingStatus;
emit statusChanged(m_status);

View File

@@ -56,6 +56,19 @@ static QMutex g_cameraMapMutex;
typedef QMap<int, AndroidCamera *> CameraMap;
Q_GLOBAL_STATIC(CameraMap, g_cameraMap)
static inline bool exceptionCheckAndClear(JNIEnv *env)
{
if (Q_UNLIKELY(env->ExceptionCheck())) {
#ifdef QT_DEBUG
env->ExceptionDescribe();
#endif // QT_DEBUG
env->ExceptionClear();
return true;
}
return false;
}
static QRect areaToRect(jobject areaObj)
{
QJNIObjectPrivate area(areaObj);
@@ -132,9 +145,9 @@ public:
Q_INVOKABLE bool init(int cameraId);
Q_INVOKABLE void release();
Q_INVOKABLE void lock();
Q_INVOKABLE void unlock();
Q_INVOKABLE void reconnect();
Q_INVOKABLE bool lock();
Q_INVOKABLE bool unlock();
Q_INVOKABLE bool reconnect();
Q_INVOKABLE AndroidCamera::CameraFacing getFacing();
Q_INVOKABLE int getNativeOrientation();
@@ -147,7 +160,7 @@ public:
Q_INVOKABLE QSize previewSize() const { return m_previewSize; }
Q_INVOKABLE void updatePreviewSize();
Q_INVOKABLE void setPreviewTexture(void *surfaceTexture);
Q_INVOKABLE bool setPreviewTexture(void *surfaceTexture);
Q_INVOKABLE bool isZoomSupported();
Q_INVOKABLE int getMaxZoom();
@@ -266,7 +279,7 @@ AndroidCamera *AndroidCamera::open(int cameraId)
worker->start();
d->moveToThread(worker);
connect(worker, &QThread::finished, d, &AndroidCameraPrivate::deleteLater);
bool ok = false;
bool ok = true;
QMetaObject::invokeMethod(d, "init", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, ok), Q_ARG(int, cameraId));
if (!ok) {
worker->quit();
@@ -289,22 +302,28 @@ int AndroidCamera::cameraId() const
return d->m_cameraId;
}
void AndroidCamera::lock()
bool AndroidCamera::lock()
{
Q_D(AndroidCamera);
QMetaObject::invokeMethod(d, "lock", Qt::BlockingQueuedConnection);
bool ok = true;
QMetaObject::invokeMethod(d, "lock", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, ok));
return ok;
}
void AndroidCamera::unlock()
bool AndroidCamera::unlock()
{
Q_D(AndroidCamera);
QMetaObject::invokeMethod(d, "unlock", Qt::BlockingQueuedConnection);
bool ok = true;
QMetaObject::invokeMethod(d, "unlock", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, ok));
return ok;
}
void AndroidCamera::reconnect()
bool AndroidCamera::reconnect()
{
Q_D(AndroidCamera);
QMetaObject::invokeMethod(d, "reconnect");
bool ok = true;
QMetaObject::invokeMethod(d, "reconnect", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, ok));
return ok;
}
void AndroidCamera::release()
@@ -368,13 +387,16 @@ void AndroidCamera::setPreviewSize(const QSize &size)
QMetaObject::invokeMethod(d, "updatePreviewSize");
}
void AndroidCamera::setPreviewTexture(AndroidSurfaceTexture *surfaceTexture)
bool AndroidCamera::setPreviewTexture(AndroidSurfaceTexture *surfaceTexture)
{
Q_D(AndroidCamera);
bool ok = true;
QMetaObject::invokeMethod(d,
"setPreviewTexture",
Qt::BlockingQueuedConnection,
Q_RETURN_ARG(bool, ok),
Q_ARG(void *, surfaceTexture ? surfaceTexture->surfaceTexture() : 0));
return ok;
}
bool AndroidCamera::isZoomSupported()
@@ -698,12 +720,12 @@ AndroidCameraPrivate::~AndroidCameraPrivate()
bool AndroidCameraPrivate::init(int cameraId)
{
m_cameraId = cameraId;
QJNIEnvironmentPrivate env;
m_camera = QJNIObjectPrivate::callStaticObjectMethod("android/hardware/Camera",
"open",
"(I)Landroid/hardware/Camera;",
cameraId);
if (!m_camera.isValid())
if (exceptionCheckAndClear(env) || !m_camera.isValid())
return false;
m_cameraListener = QJNIObjectPrivate(g_qtCameraListenerClass, "(I)V", m_cameraId);
@@ -731,26 +753,25 @@ void AndroidCameraPrivate::release()
m_camera.callMethod<void>("release");
}
void AndroidCameraPrivate::lock()
bool AndroidCameraPrivate::lock()
{
QJNIEnvironmentPrivate env;
m_camera.callMethod<void>("lock");
return !exceptionCheckAndClear(env);
}
void AndroidCameraPrivate::unlock()
bool AndroidCameraPrivate::unlock()
{
QJNIEnvironmentPrivate env;
m_camera.callMethod<void>("unlock");
return !exceptionCheckAndClear(env);
}
void AndroidCameraPrivate::reconnect()
bool AndroidCameraPrivate::reconnect()
{
QJNIEnvironmentPrivate env;
m_camera.callMethod<void>("reconnect");
if (env->ExceptionCheck()) {
#ifdef QT_DEBUG
env->ExceptionDescribe();
#endif // QT_DEBUG
env->ExceptionDescribe();
}
return !exceptionCheckAndClear(env);
}
AndroidCamera::CameraFacing AndroidCameraPrivate::getFacing()
@@ -832,11 +853,13 @@ void AndroidCameraPrivate::updatePreviewSize()
emit previewSizeChanged();
}
void AndroidCameraPrivate::setPreviewTexture(void *surfaceTexture)
bool AndroidCameraPrivate::setPreviewTexture(void *surfaceTexture)
{
QJNIEnvironmentPrivate env;
m_camera.callMethod<void>("setPreviewTexture",
"(Landroid/graphics/SurfaceTexture;)V",
static_cast<jobject>(surfaceTexture));
return !exceptionCheckAndClear(env);
}
bool AndroidCameraPrivate::isZoomSupported()
@@ -1020,8 +1043,7 @@ void AndroidCameraPrivate::setFocusAreas(const QList<QRect> &areas)
arrayList.callMethod<jboolean>("add",
"(Ljava/lang/Object;)Z",
rectToArea(areas.at(i)).object());
if (env->ExceptionCheck())
env->ExceptionClear();
exceptionCheckAndClear(env);
}
list = arrayList;
}
@@ -1347,9 +1369,11 @@ void AndroidCameraPrivate::fetchLastPreviewFrame()
void AndroidCameraPrivate::applyParameters()
{
QJNIEnvironmentPrivate env;
m_camera.callMethod<void>("setParameters",
"(Landroid/hardware/Camera$Parameters;)V",
m_parameters.object());
exceptionCheckAndClear(env);
}
QStringList AndroidCameraPrivate::callParametersStringListMethod(const QByteArray &methodName)
@@ -1386,10 +1410,8 @@ static JNINativeMethod methods[] = {
bool AndroidCamera::initJNI(JNIEnv *env)
{
jclass clazz = env->FindClass("org/qtproject/qt5/android/multimedia/QtCameraListener");
if (env->ExceptionCheck())
env->ExceptionClear();
if (clazz) {
if (!exceptionCheckAndClear(env) && clazz) {
g_qtCameraListenerClass = static_cast<jclass>(env->NewGlobalRef(clazz));
if (env->RegisterNatives(g_qtCameraListenerClass,
methods,

View File

@@ -90,9 +90,9 @@ public:
int cameraId() const;
void lock();
void unlock();
void reconnect();
bool lock();
bool unlock();
bool reconnect();
void release();
CameraFacing getFacing();
@@ -106,7 +106,7 @@ public:
QSize previewSize() const;
void setPreviewSize(const QSize &size);
void setPreviewTexture(AndroidSurfaceTexture *surfaceTexture);
bool setPreviewTexture(AndroidSurfaceTexture *surfaceTexture);
bool isZoomSupported();
int getMaxZoom();