qt androidextras getMemoryInfo MemoryInfo availMem object field crash from c++

470 views Asked by At

How to call getMemoryInfo from c++ qt 5.6 android? Im not sure about casting from java/lang/Object to android/app/ActivityManager when calling getSystemService api, but I get valid QAndroidJniObject btw. Here is my code so far.

osinfoandroid.h

#ifndef OSINFOANDROID_H
#define OSINFOANDROID_H

#include <QObject>

class OsInfoAndroid : public QObject {
    Q_OBJECT
public:
explicit OsInfoAndroid(QObject *parent = 0);
~OsInfoAndroid();
public Q_SLOTS:

void testgetmeminfo();
};

#endif  // OSINFOANDROID_H

osinfoandroid.cpp

#include "osinfoandroid.h"
#include <QDebug>
#include <QtAndroidExtras/QtAndroidExtras>

OsInfoAndroid::OsInfoAndroid(QObject *parent) : QObject(parent) {}

OsInfoAndroid::~OsInfoAndroid() {}

void OsInfoAndroid::testgetmeminfo() {
QAndroidJniObject cntact_service = QAndroidJniObject::getStaticObjectField(
  "android/content/Context", "ACTIVITY_SERVICE", "Ljava/lang/String;");
if (cntact_service.isValid()) {
qDebug() << Q_FUNC_INFO << "ACTIVITY_SERVICE OK GOT INTERNAL STRING";
} else {
    qDebug() << Q_FUNC_INFO << "ACTIVITY_SERVICE sad got nothing :(";
}
QAndroidJniObject ctx = QtAndroid::androidActivity().callObjectMethod(
  "getApplicationContext", "()Landroid/content/Context;");
if (ctx.isValid()) {
qDebug() << Q_FUNC_INFO << "got valid context";

} else {
    qDebug() << Q_FUNC_INFO << "sad got nothing context";
}

QAndroidJniObject actmgr = ctx.callObjectMethod(
  "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;",
  cntact_service.object<jstring>());
if (actmgr.isValid()) {
qDebug() << Q_FUNC_INFO << "ok got ActivityManager";
QAndroidJniObject meminfo("android/app/ActivityManager$MemoryInfo");
if(!meminfo.isValid ()){
    qDebug()<<Q_FUNC_INFO<<"SAD GOT INVALID MemoryInfo!";
    return;
}
qDebug()<<Q_FUNC_INFO<<"ok got valid MemoryInfo";
//CRASH HERE
QAndroidJniObject voidmeminfo = actmgr.callObjectMethod(
    "getMemoryInfo", "(Landroid/app/ActivityManager$MemoryInfo;)V",
    meminfo.object<jclass>());
    if (voidmeminfo.isValid()) {
  qDebug() << Q_FUNC_INFO << "OK CALLING getMemoryInfo FROM CPP SUKSES!! ";
    } else {
  qDebug() << Q_FUNC_INFO << "calling from c++ failed!!";
    }

 }
}

main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <osinfoandroid.h>

int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);

QQmlApplicationEngine engine;
OsInfoAndroid osinfo;
engine.rootContext ()->setContextProperty ("osinfo",&osinfo);
engine.load(QUrl(QLatin1String("qrc:/main.qml")));

return app.exec();
}

AndroidManifest.xml

<?xml version="1.0"?>
<manifest package="org.androidgetmeminfocrash" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="1.0" android:versionCode="1" android:installLocation="auto">
<application android:hardwareAccelerated="true" android:name="org.qtproject.qt5.android.bindings.QtApplication" android:label="-- %%INSERT_APP_NAME%% --">
    <activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation" android:name="org.qtproject.qt5.android.bindings.QtActivity" android:label="-- %%INSERT_APP_NAME%% --" android:screenOrientation="unspecified" android:launchMode="singleTop">
        <intent-filter>
            <action android:name="android.intent.action.MAIN"/>
            <category android:name="android.intent.category.LAUNCHER"/>
        </intent-filter>
        <meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/>
        <meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/>
        <meta-data android:name="android.app.repository" android:value="default"/>
        <meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
        <meta-data android:name="android.app.bundled_libs_resource_id" android:resource="@array/bundled_libs"/>
        <!-- Deploy Qt libs as part of package -->
        <meta-data android:name="android.app.bundle_local_qt_libs" android:value="-- %%BUNDLE_LOCAL_QT_LIBS%% --"/>
        <meta-data android:name="android.app.bundled_in_lib_resource_id" android:resource="@array/bundled_in_lib"/>
        <meta-data android:name="android.app.bundled_in_assets_resource_id" android:resource="@array/bundled_in_assets"/>
        <!-- Run with local libs -->
        <meta-data android:name="android.app.use_local_qt_libs" android:value="-- %%USE_LOCAL_QT_LIBS%% --"/>
        <meta-data android:name="android.app.libs_prefix" android:value="/data/local/tmp/qt/"/>
        <meta-data android:name="android.app.load_local_libs" android:value="-- %%INSERT_LOCAL_LIBS%% --"/>
        <meta-data android:name="android.app.load_local_jars" android:value="-- %%INSERT_LOCAL_JARS%% --"/>
        <meta-data android:name="android.app.static_init_classes" android:value="-- %%INSERT_INIT_CLASSES%% --"/>
        <!--  Messages maps -->
        <meta-data android:value="@string/ministro_not_found_msg" android:name="android.app.ministro_not_found_msg"/>
        <meta-data android:value="@string/ministro_needed_msg" android:name="android.app.ministro_needed_msg"/>
        <meta-data android:value="@string/fatal_error_msg" android:name="android.app.fatal_error_msg"/>
        <!--  Messages maps -->

        <!-- Splash screen -->
        <!--
        <meta-data android:name="android.app.splash_screen_drawable" android:resource="@drawable/logo"/>
        -->
        <!-- Splash screen -->

        <!-- Background running -->
        <!-- Warning: changing this value to true may cause unexpected crashes if the
                      application still try to draw after
                      "applicationStateChanged(Qt::ApplicationSuspended)"
                      signal is sent! -->
        <meta-data android:name="android.app.background_running" android:value="false"/>
        <!-- Background running -->

        <!-- auto screen scale factor -->
        <meta-data android:name="android.app.auto_screen_scale_factor" android:value="false"/>
        <!-- auto screen scale factor -->
    </activity>
</application>
<uses-sdk android:minSdkVersion="9" android:targetSdkVersion="14"/>
<supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/>

<!-- The following comment will be replaced upon deployment with default permissions based on the dependencies of the application.
     Remove the comment if you do not require these default permissions. -->
<!-- %%INSERT_PERMISSIONS -->

<!-- The following comment will be replaced upon deployment with default features based on the dependencies of the application.
     Remove the comment if you do not require these default features. -->
<!-- %%INSERT_FEATURES -->

<uses-permission android:name="android.permission.GET_TASKS"/>
    <uses-permission android:name="android.permission.HARDWARE_TEST"/>
</manifest>

the whole project can be download here

Ok the problem looked like coming from calling here

QAndroidJniObject voidmeminfo = actmgr.callObjectMethod(
    "getMemoryInfo", "(Landroid/app/ActivityManager$MemoryInfo;)V",
    meminfo.object<jclass>());

could be changed to

actmgr.callMethod<void>(
    "getMemoryInfo", "(Landroid/app/ActivityManager$MemoryInfo;)V",
    meminfo.object<jobject>());

but I'm not really sure which one is correct whether using meminfo.object< jobject >()); or meminfo.object< jclass >());

So, I want to get android MemoryInfo availMem object field, after calling getMemoryInfo function I add this code

QAndroidJniObject availmem=meminfo.getObjectField("availMem","J");
if(!availmem.isValid ()){
   qDebug() << Q_FUNC_INFO << "calling from c++ failed!!";
}

But, above code still producing obvious crash on android jelly bean, any pointers?

The equivalence java code I want to achieve is something like this one

ActivityManager activityManager = (ActivityManager)     context.getSystemService(ACTIVITY_SERVICE);
MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
activityManager.getMemoryInfo(memoryInfo);

Log.i(TAG, " memoryInfo.availMem " + memoryInfo.availMem + "\n" );

So now, I'm using second approach with full java body function based on above project source download. Try emitting valid activity context from c++ and invoke plain static method in java. And then create folder com -> getmemorycrash folder . And I create a java file OsInfo.java, so now my getmemoryinfo.pro become like this

QT += qml quick androidextras

CONFIG += c++11

SOURCES += main.cpp osinfoandroid.cpp
HEADERS +=  osinfoandroid.h

RESOURCES += qml.qrc

# Additional import path used to resolve QML modules in Qt Creator's code model
QML_IMPORT_PATH =

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

DISTFILES += \
    android/AndroidManifest.xml \
    android/gradle/wrapper/gradle-wrapper.jar \
    android/gradlew \
    android/res/values/libs.xml \
    android/build.gradle \
    android/gradle/wrapper/gradle-wrapper.properties \
    android/gradlew.bat \
    android/src/com/getmemorycrash/OsInfo.java
ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android

OsInfo.java

package com.getmemorycrash;
import java.lang.Object;
import android.app.ActivityManager;
import android.app.ActivityManager.MemoryInfo;
import java.lang.String;
import android.content.Context;
import android.util.Log;

public class OsInfo {
 static long getMemavail(Context context ){
    Log.d("ada","executing getMemavail");
    ActivityManager activityManager = (ActivityManager)     context.getSystemService(Context.ACTIVITY_SERVICE);
    MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
    activityManager.getMemoryInfo(memoryInfo);

    Log.i("mydebug", " memoryInfo.availMem " + memoryInfo.availMem + "\n" );
    return memoryInfo.availMem;
 }
}

The final osinfoandroid.cpp, I add testgetmeminfomethod2 function with following code

void OsInfoAndroid::testgetmeminfomethod2()
{
QAndroidJniObject ctx = QtAndroid::androidActivity().callObjectMethod(
    "getApplicationContext", "()Landroid/content/Context;");
if (ctx.isValid()) {
  qDebug() << Q_FUNC_INFO << "got valid context";
} else {
  qDebug() << Q_FUNC_INFO << "sad got nothing context";
}
//crash here
QAndroidJniObject availmem=QAndroidJniObject::callStaticObjectMethod("com/getmemorycrash/OsInfo",
                                                                     "getMemavail",
                                                                     "(Landroid/content/Context;)J",
                                                                     ctx.object<jclass>());
if(!availmem.isValid ()){
      qDebug() << Q_FUNC_INFO << "calling from c++ failed!!";
 }  
}

So, my question is Why above function becomes segfault again? The java code is executed until Log android function and then an immediate crash happens exactly at return ?

Thanks

1

There are 1 answers

0
user3453753 On

ok, I eventually can fix this, the correct function to get public member java class variable if they are primitive types, I can use this based on above case using the first approach instead of relaying on QAndroidJniObject

jlong availmem=meminfo.getField<jlong>("availMem");