QApplication on Android, started from JNI leads up to SEGFAULT - android-studio

The main goal is to develop an .apk in Android Studio with qt based shared libraries (.so) onboard and start hidden qt event loop (QCoreApplication) in there.
The first of all,
I were concerned on how to make an Android archive (.aar) in QtCreator to use that in any target .apk as is - but it's wrong way because QtCreator doesn't support Android archives from the box. By the way to make a .aar instead of .apk we should change build.gradle inside cmake build directory like on the picture
But now its no matter from where we'll get .so to bring them into Android Studio project.
Android Studio project
Then we should create an activity based AndroidStudio project and then a SdkService class with native method initSdk that should be called to start our hidden native processing. Here is the path of mentioned class: AndroidStudioProjects/MyApplication7/app/src/main/java/io/company/companySdk/SdkService.java
AndroidStudio project overview
JNI header
Then to generate JNI header I used javac:
$ pwd
/home/rozhkov/AndroidStudioProjects/MyApplication7/app/src/main/java/io/company/companySdk
$ javac -h . SdkService.java
Here is initSdk definition to include it in qtcreator project from io_company_companySdk_SdkService.h:
extern "C"
JNIEXPORT void JNICALL Java_io_company_companySdk_SdkService_initSdk
(JNIEnv *, jclass);
QtCreator project
I created Qt Widgets Application because qtcreator generates androiddeployqt step for that kind of project. CMAKE type of project is most common for me.
It's important to not take off linking with Widgets qt library
Here are simple CMakeLists.txt and main.cpp following:
CMakeLists.txt
make_minimum_required(VERSION 3.5)
project(sample_service VERSION 0.1 LANGUAGES CXX)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Widgets REQUIRED)
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Widgets REQUIRED)
set(PROJECT_SOURCES
main.cpp
)
if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
qt_add_executable(sample_service
MANUAL_FINALIZATION
${PROJECT_SOURCES}
)
endif()
target_link_libraries(sample_service PRIVATE Qt${QT_VERSION_MAJOR}::Widgets)
if(QT_VERSION_MAJOR EQUAL 6)
qt_finalize_executable(sample_service)
endif()
main.cpp
#include <thread>
#include <memory>
#include <jni.h>
#include <android/log.h>
#include <QCoreApplication>
#include <QJniEnvironment>
extern "C"
JNIEXPORT void JNICALL
Java_io_company_companySdk_SdkService_initSdk(JNIEnv *, jclass);
class ServiceHolder
{
public:
typedef std::unique_ptr<std::thread> UniqueThreadPtr;
static void init_app_worker() {
if (_appThread)
return;
_appThread = std::make_unique<std::thread>([]() {
int argc = 0;
using namespace std::chrono_literals;
QCoreApplication app(argc, nullptr);
app.exec();
});
}
private:
static UniqueThreadPtr _appThread;
};
ServiceHolder::UniqueThreadPtr ServiceHolder::_appThread;
extern "C"
JNIEXPORT void JNICALL
Java_io_company_companySdk_SdkService_initSdk(JNIEnv * env, jclass)
{
int argc = 0;
__android_log_print(ANDROID_LOG_VERBOSE, "SdkConnect", "Java_io_company_companySdk_SdkService_initSdk");
if (QJniEnvironment::checkAndClearExceptions(env, QJniEnvironment::OutputMode::Verbose))
{
__android_log_print(ANDROID_LOG_VERBOSE, "SdkConnect", "Java environment checked");
}
else
{
__android_log_print(ANDROID_LOG_VERBOSE, "SdkConnect", "Java environment not checked");
}
ServiceHolder::init_app_worker();
}
jint JNI_OnLoad(JavaVM * aVm, void * aReserved)
{
__android_log_print(ANDROID_LOG_INFO, "SdkConnect", "Company sdk on load");
return JNI_VERSION_1_6;
}
Deploy to Android Studio
Qtcreator's build result is a android-build-debug.apk in cmake build directory. Right from there we should copy all related .so into jniLibs special directory from where that shared libs should be loaded as part of target .apk.
The following instructions discover how it can be done:
$ pwd
/home/rozhkov/sources/android/sample_service/build-sample_service-Android_Qt_6_1_2_Clang_x86_64-Release/android-build/build/outputs/apk/debug
$ mkdir extracted
$ unzip -qod extracted/ android-build-debug.apk
$ cp extracted/lib/x86_64/* ~/AndroidStudioProjects/MyApplication7/app/src/main/jniLibs/x86_64/
$ mv ~/AndroidStudioProjects/MyApplication7/app/src/main/jniLibs/x86_64/libsample_service_x86_64.so ~/AndroidStudioProjects/MyApplication7/app/src/main/jniLibs/x86_64/libsample_service.so
$ ls ~/AndroidStudioProjects/MyApplication7/app/src/main/jniLibs/x86_64/
libc++_shared.so libplugins_imageformats_qjpeg_x86_64.so libplugins_styles_qandroidstyle_x86_64.so libQt6Network_x86_64.so
libplugins_imageformats_qgif_x86_64.so libplugins_networkinformationbackends_androidnetworkinformationbackend_x86_64.so libQt6Core_x86_64.so libQt6Widgets_x86_64.so
libplugins_imageformats_qico_x86_64.so libplugins_platforms_qtforandroid_x86_64.so libQt6Gui_x86_64.so libsample_service.so
Call natives from Java
The next step is loading shared library from Java and call mentioned initSdk. I extened MainActivity class in the following way:
Call natives from java
Running Android application - SEGFAULT
At last our Android application sample running on x86_64 emulator device.
Always when sample .apk was running there happened SEGFAULT in libsample_service.so on QCoreApplication constructor.
In stack-trace we can see it happens exactly in QJniEnvironmentPrivate:
stack-trace
There is no matter if it used Qt 6.1.2 or, for example, 5.1.15 - that segfault happened always.
Questions
There are remaining options to do that I not made in time.
For example may be that happens because of app version not set.
#04 pc 000000000032da4a /data/app/rozhkov.example.myapplication-2/lib/x86_64/libQt6Core_x86_64.so (_ZNK23QCoreApplicationPrivate10appVersionEv+372)
Or may be it is QtAndroidService is better for that kind of tasks.
Or may be anything else...
Does anyone know how it should be done?

The following steps guide you in proper way how to manage Android Studio project to make possible start QtService with some service activities on native side of android application.
Qt and QtCreator:
There are QtAndroidExtras in Qt 5.15, so you should build project under this version.
In your build configuration you should enable a few build options for any architecture that you need.
To start QtService you can use simple code:
#include <android/log.h>
#include <QtAndroidExtras/QAndroidService>
int main(int argc, char ** argv) {
QAndroidService app(argc, argv);
__android_log_print(ANDROID_LOG_INFO, "Sample service", "Service being started");
return app.exec();
}
Then in Android Studio project you need to:
Create jniLibs folder and copy all .so architecture related sets, that qt placed in android-build/libs this way:
[# libs]$ pwd
/home/rozhkov/sources/android/service_apk/build-service_apk-Android_Qt_5_15_0_Clang_Multi_Abi_369ced-Release/android-build/libs
[# libs]$ ls
arm64-v8a armeabi-v7a QtAndroidBearer.jar QtAndroidExtras.jar QtAndroid.jar x86 x86_64
[# libs]$ cp -R x86* arm* ~/AndroidStudioProjects/MyApplication7/app/src/main/jniLibs/
[# libs]$
Copy all .jar files:
[# libs]$ cp QtAndroid*.jar ~/AndroidStudioProjects/MyApplication7/app/src/main/java/jar/
You'll need related .aidls from Qt. Copy them too:
[# src]$ pwd
/home/rozhkov/Qt/5.15.0/Src/qtbase/src/android/java/src
[# src]$ cp -R org ~/AndroidStudioProjects/MyApplication7/app/src/main/aidl/
And resource libs.xml from qt build directory:
[# values]$ pwd
/home/rozhkov/sources/android/service_apk/build-service_apk-Android_Qt_5_15_0_Clang_Multi_Abi_369ced-Release/android-build/res/values
[# values]$ cp libs.xml ~/AndroidStudioProjects/MyApplication7/app/src/main/res/values/
Pupulate AndroidManifest.xml like in official Qt guide [https://doc.qt.io/qt-5/android-services.html], but you need replace %% instractions manually. I got that values from AndroidManifest of .apk, built under Qt
It's of mine:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="rozhkov.example.myapplication">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/Theme.MyApplication"
>
<activity
android:name=".MainActivity"
android:label="#string/app_name"
android:theme="#style/Theme.MyApplication.NoActionBar" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name="io.company.companySdk.QtAndroidService" android:enabled="true"
android:exported="true" android:process=":qt">
<meta-data android:name="android.app.lib_name" android:value="service_apk"/>
<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="1"/>
<!-- Run with local libs -->
<meta-data android:name="android.app.use_local_qt_libs" android:value="1"/>
<meta-data android:name="android.app.libs_prefix" android:value="/data/local/tmp/qt/"/>
<meta-data android:name="android.app.load_local_libs_resource_id" android:resource="#array/load_local_libs"/>
<meta-data android:name="android.app.load_local_jars" android:value="jar/QtAndroid.jar:jar/QtAndroidExtras.jar"/>
<meta-data android:name="android.app.static_init_classes" android:value=""/>
<!-- Run with local libs -->
<!-- Background running -->
<meta-data android:name="android.app.background_running" android:value="true"/>
<!-- Background running -->
</service>
</application>
</manifest>
At last you should define a java class, inherits QtService. I got it in Qt docs:
package io.company.companySdk;
import android.content.Intent;
import android.util.Log;
import org.qtproject.qt5.android.bindings.QtService;
public class QtAndroidService extends QtService
{
private static final String TAG = "QtAndroidService";
#Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "Creating Service");
}
#Override
public void onDestroy() {
super.onDestroy();
Log.i(TAG, "Destroying Service");
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
int ret = super.onStartCommand(intent, flags, startId);
return ret;
}
}
and start it from default AndroidStudio MainActivity:
...
import io.company.companySdk.QtAndroidService;
...
public class MainActivity extends AppCompatActivity {
...
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
startService(new Intent(this, QtAndroidService.class));
...
Now it should be all right ;)

Related

TarsosDSP in Android Studio

This is my first post on SO and I am trying to combine my music skills with computer science.
I am using Android studio 3.1.2 with gradle 4.5, Nexus 5X, API 25, Android 7.1.1, Windows 7.
I followed very careful these instructions:
Create a project called Pitchbender
Download the .jar of TarsosDSP and included in
C:\Users\Carlos\AndroidStudioProjects\Pitchbender\app\libs\TarsosDSP-Android-latest
I checked the build.gradle of my project:
dependencies { implementation fileTree(dir: ‘libs’, include: [‘*.jar’]) }
In my project, I have the following imports automatically done by Android Studio:
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import be.tarsos.dsp.AudioEvent
import be.tarsos.dsp.io.android.AudioDispatcherFactory
import be.tarsos.dsp.pitch.PitchDetectionHandler
import be.tarsos.dsp.pitch.PitchDetectionResult
import be.tarsos.dsp.pitch.PitchProcessor
import kotlinx.android.synthetic.main.activity_main.*
import be.tarsos.dsp.pitch.PitchProcessor.PitchEstimationAlgorithm
import be.tarsos.dsp.AudioProcessor
import android.widget.TextView
import be.tarsos.dsp.AudioDispatcher
I have this permission in my manifest file
uses-permission android:name=”android.permission.RECORD_AUDIO”
Android Studio gives the option to convert to Kotlin the first line of the following code:
AudioDispatcher dispatcher = AudioDispatcherFactory.fromDefaultMicrophone(22050,1024,0);
If I respond to “No” to the Kotlin conversion, I have the following compilation error:
Clasifier “AudioDispatcher” does not have any companion object, and thus must be initialized here.
What can I do?
If I respond “Yes” to the Kotlin conversion question, that statement is converted to
val dispatcher = AudioDispatcherFactory.fromDefaultMicrophone(22050, 1024, 0)
and then, when I run this program, Android informs me that there is an error and closes my project and keeps closing my project. What to do?
Please help to run at least that first instruction of the complete code:
PitchDetectionHandler pdh = new PitchDetectionHandler() {
#Override
public void handlePitch(PitchDetectionResult res, AudioEvent e){
final float pitchInHz = res.getPitch();
runOnUiThread(new Runnable() {
#Override
public void run() {
processPitch(pitchInHz);
}
});
}
};
AudioProcessor pitchProcessor = new PitchProcessor(PitchEstimationAlgorithm.FFT_YIN, 22050, 1024, pdh);
dispatcher.addAudioProcessor(pitchProcessor);
Thread audioThread = new Thread(dispatcher, "Audio Thread");
audioThread.start();
Question:
Do you have any simple project in Android Studio, so that I can see what my errors are?
I had a similar problem when I tried to run this example and my solution (Sep. 2019) was to add a runtime confirmation of the record permission. I'm not sure if it's the same case, buuuut
Here is my code to it:
private boolean permissionToRecordAccepted = false;
private String [] permissions = {Manifest.permission.RECORD_AUDIO};
private static final int REQUEST_RECORD_AUDIO_PERMISSION = 200;
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode){
case REQUEST_RECORD_AUDIO_PERMISSION:
permissionToRecordAccepted = grantResults[0] == PackageManager.PERMISSION_GRANTED;
break;
}
if (!permissionToRecordAccepted ) finish();
}

Scan bluetooth devices not working on Android Things DP3

Scan Bluetooth Devices not working on Android Things DP3. Any idea/workaround? Not getting action BluetoothDevice.ACTION_FOUND.
I've added ACCESS_FINE_LOCATION and ACCESS_COARSE_LOCATION permisssion due changes in Android 6.0 but still not working.
https://developer.android.com/about/versions/marshmallow/android-6.0-changes.html
Also android.permission.BLUETOOTH_ADMIN, android.permission.BLUETOOTH
I have checked, ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION over Android >=6.0 needs runtime permission but it give an error due Android Things limitation.
private void checkPermission() {
List<String> permissionsList = new ArrayList<String>();
List<String> permissionsNeeded = new ArrayList<String>();
// app
permissionsList.add(Manifest.permission.ACCESS_FINE_LOCATION);
permissionsList.add(Manifest.permission.ACCESS_COARSE_LOCATION);
permissionsList.add(Manifest.permission.ACCESS_WIFI_STATE);
permissionsList.add(Manifest.permission.SET_TIME_ZONE);
// AndroidThingsLib
permissionsList.add(Manifest.permission.BLUETOOTH_ADMIN);
permissionsList.add(Manifest.permission.BLUETOOTH);
permissionsList.add(Manifest.permission.CHANGE_WIFI_STATE);
permissionsList.add(Manifest.permission.INTERNET);
permissionsList.add(Manifest.permission.ACCESS_NETWORK_STATE);
for (int i = 0; i < permissionsList.size(); i++){
if (checkSelfPermission(permissionsList.get(i)) == PackageManager.PERMISSION_GRANTED) {
permissionsNeeded.add(permissionsList.get(i));
updateDataLogs(SourceEnum.System, "GRANTED: " + permissionsList.get(i));
} else {
permissionsNeeded.add(permissionsList.get(i));
updateDataLogs(SourceEnum.Error, "NOT GRANTED: " + permissionsList.get(i));
}
}
if (permissionsList.size()>0){
requestPermissions(permissionsNeeded.toArray(new String[permissionsNeeded.size()]),5678);
}
}
#Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (requestCode == 5678){
for (int i = 0; i < permissions.length; i++){
updateDataLogs(SourceEnum.Error, permissions[i] + " result " + grantResults[i]);
}
}
}
private void enableBluetoothScan() {
updateDataLogs(SourceEnum.System, "enableBluetoothScan");
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothDevice.ACTION_FOUND);
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
registerReceiver(mReceiver, filter);
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (bluetoothAdapter.isEnabled()){
bluetoothAdapter.startDiscovery();
} else {
updateDataLogs(SourceEnum.Error, "Bluetooth Adapter not enabled");
}
}
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)) {
updateDataLogs(SourceEnum.System, "DISCOVERY STARTED");
} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
updateDataLogs(SourceEnum.System, "DISCOVERY FINISHED");
} else if (BluetoothDevice.ACTION_FOUND.equals(action)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
updateDataLogs(SourceEnum.System, "NEW DEVICE " + device.getName());
}
}
}
};
As noted in the Platform Overview, runtime permissions granting is not supported on Android Things because the device may not have an interactive display to show the dialog. Instead, Android Things falls back on the install-time permissions model for all permissions requested in the manifest. This means calls to methods like requestPermissions() aren't necessary and won't help you.
Regarding issues encountered during the current preview, the following excerpt is from the Release Notes:
Dangerous permissions requested by apps are not granted until the next device reboot. This includes new app installs and new elements in existing apps.
For the time being, when your app requests dangerous permissions (like location permissions), you need to reboot the device after install (one-time) to have those permissions granted during boot.
Thanks to your explanation what you did already, I was able to conquer this issue. Because you did not mark the question as solved yet:
build.gradle (app; thanks to 1 and 2):
apply plugin: 'com.android.application'
android {
compileSdkVersion 24
<!-- update this to your version of the Android SDK Tools -->
buildToolsVersion "25.0.3"
defaultConfig {
minSdkVersion 24
}
}
dependencies {
<!-- update this to your version of Android Things -->
provided 'com.google.android.things:androidthings:0.4-devpreview'
}
AndroidManifest.xml (thanks to 1 and 2):
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package=" . . . ">
<!-- required for getState in startDiscovery -->
<uses-permission android:name="android.permission.BLUETOOTH"/>
<!-- required for startDiscovery -->
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<!-- required for the IntentFilter ACTION_FOUND -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<!-- change to your label -->
<application android:label="Intel Edison">
<uses-library android:name="com.google.android.things"/>
<!-- change to the name of your Main Activity -->
<activity android:name=".HomeActivity">
<!-- Launch activity as default from Android Studio -->
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<!-- Launch activity automatically on boot -->
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.IOT_LAUNCHER"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
</application>
</manifest>
HomeActivity.java:
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
filter.addAction(BluetoothDevice.ACTION_FOUND);
filter.addAction(BluetoothDevice.ACTION_NAME_CHANGED);
registerReceiver(mReceiverBT, filter);
BluetoothAdapter mBtAdapter = BluetoothAdapter.getDefaultAdapter();
boolean started = mBtAdapter.startDiscovery();
if (false == started) {
<!-- in that case, catch BluetoothAdapter.STATE_ON in the receiver and start the Bluetooth Discovery then -->
mBtAdapter.enable();
}
With this code, my app started right away after booting the Intel Edison board and was able to discover nearby Bluetooth devices. Notes:
On this board, Android Things requires 69 seconds (Developer Preview 4) or 82 seconds (Developer Preview 3) to start my app.
In logcat, I saw "Not granting permission android.permission.ACCESS_COARSE_LOCATION to package … because it was previously installed without". I found no way around that; reported in Issue 62514750.
Actually, you might not need any ‘dangerous’ permission at all – for example, I do nothing on ACTION_FOUND – because ACTION_NANE_CHANGED is fired always here in my scenarios; reported in Issue 62524160.

Google-play-services and INSTALL_PARSE_FAILED_MANIFEST_MALFORMED

I'm migrating from Eclipse to Android Studio. When I integrate Google-play-services into my project, I fail to install the APK to a device with the following error: "INSTALL_PARSE_FAILED_MANIFEST_MALFORMED". How come eerrApparently Android Studio and this library has a compatibility issue with my legacy app's package name which starts with an uppercase letter. When I change the first letter of the package name (ApplicationId in Android Studio) to lower, everything is fine. Note that my base package name is all lowercase. The problem is with ApplicationId.
The app is live on the store for a few years now and I don't want to upload it as a new app.
Really stuck on this - is there a workaround?
Please refer to Naming Conventions
You can not simply use upper case with package name anymore.
This worked for me:
Instead of importing the eclipse project into Android Studio,
(1) Create a new Android Studio project with the package name that you have already put on the Google Play Store but use all lower case.
(2) Then copy all your files into the Android Studio project.
(3) Change the package ID in the build.gradle (Module App) file with the original package name with upper case (see below) and sync (this will create the package name in the apk with the original package name with upper case chars and it will upload to the Google Play Store and replace your old one!!!
(4) This installed the apk (with uppercase package name) onto my test phone and it worked perfectly.
apply plugin: 'com.android.application'
android {
compileSdkVersion 21
buildToolsVersion "21.1.2"
defaultConfig {
applicationId "DrWebsterApps.New.England.Patriots.Game"
minSdkVersion 9
targetSdkVersion 21
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile 'com.android.support:appcompat-v7:21.0.0'
compile 'com.google.android.gms:play-services:7.0.0'
}
(5) as a test case here is code that pops up the currently running app's package name to proof it:
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ListActivity;
import android.app.ActivityManager.RunningAppProcessInfo;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.util.List;
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/* put this in the manifest.xml file!!!
<uses-permission android:name="android.permission.GET_TASKS"/>
*/
ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
List<ActivityManager.RunningTaskInfo> taskInfo = am.getRunningTasks(1);
createDialogAnswer(taskInfo.get(0).topActivity.getPackageName());
}
public void createDialogAnswer(String msg) {
AlertDialog.Builder adb;
LinearLayout linear = new LinearLayout(this);
linear.setBackgroundColor(Color.RED);
linear.setOrientation(LinearLayout.VERTICAL);
adb = new AlertDialog.Builder(this);
// String team = (string)R.string.app_name;
adb.setTitle(" Trivia App");
adb.setView(linear);
LayoutInflater mInflater = (LayoutInflater) this
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View PopUpView = mInflater.inflate(R.layout.scores, linear);
TextView mytext = (TextView) PopUpView.findViewById(R.id.QPopUp);
// mytext.setTextColor(getResources().getColor(R.color.White));
// mytext.setTextSize(getResources().getDimension(R.dimen.aboutApp_text_size));
mytext.append(msg + " \n");
adb.setNegativeButton("Quit", myDialogOnClickListener);
adb.show();
}
DialogInterface.OnClickListener myDialogOnClickListener = new DialogInterface.OnClickListener() {
public void onClick(DialogInterface v, int x) {
finish();
}
};

Android Studio, Kiosk mode, Single-Purpose Devices, Lock Task mode

I'm trying to create a single purpose app.
So I have create an BaseActivity that all my activities inherit from it.
it's look like
public class LockDeviceActivity extends AppCompatActivity {
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
private void startLock() {
if(mDevicePolicyManager.isLockTaskPermitted(getPackageName())) {
/**
* If lock task is permitted, we can lock the task. We can use an external DPM like
* TestDPC provided by Google to manage lock task list.
*
* If the lock is obtained using TestDPC, features like status bar, home button, recent
* apps, etc is disabled.
*
* To unlock we can programatically call stopLockTask() when users taps a button. But
* in practice this should be done using a separate admin console or Confirm Credential.
*
* For API 23+ you can check if the lock is active by checking if
* activityManager.getLockTaskModeState() == ActivityManager.LOCK_TASK_MODE_NONE
*/
Log.d(TAG, "startLock() called");
this.startLockTask();
} else {
/**
* The device is not whitelisted.
*/
Toast.makeText(this, "The app is not whitelisted for lock", Toast.LENGTH_SHORT).show();
// Timber.d("The app is not whitelisted for lock task");
/**
* We can still pin the app but it will not be locked.
*
* We can simply unlock by pressing recent and back button together.
*
* Unlocking by calling stopLockTask() on button click can be achieved as well.
*/
// Timber.d("just pinning the app");
this.startLockTask();
}
}
so when I first Enter the app, I can see some Pinned message and it's okay by me.
The problem is, when I'm doing an Intent from one activity to other Activity which contain Fragment, I'm getting the following image :
plus i'm getting a some system toast message :
" the app is not whitelisted for lock "
how can I avoid this kind of behavior ?
thank you all .
1) Reset device to factory default
2) Skip Account registration
3) Install the app then close it
4) Run adb shell dpm set-device-owner <app-package-name>/.<AppAdminReceiver>
5) Make sure you get a successful result.
6) Re-launch the app.
The following must be satisfied:
AndroidManifest.xml inside application tag
<activity
android:name=".AppActivity"
android:label="Locked Activity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.HOME"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
<receiver
android:name=".AppAdminReceiver"
android:description="#string/app_name"
android:label="#string/app_name"
android:permission="android.permission.BIND_DEVICE_ADMIN">
<meta-data
android:name="android.app.device_admin"
android:resource="#xml/device_admin_receiver" />
<intent-filter>
<action android:name="android.intent.action.DEVICE_ADMIN_ENABLED"/>
<action android:name="android.intent.action.PROFILE_PROVISIONING_COMPLETE"/>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
Create a new Java Class named AppAdminReceiver.java (it's fine to leave it blank)
public class AppAdminReceiver extends android.app.admin.DeviceAdminReceiver {
}
Add device_admin_receiver.xml in /res/xml directory
<?xml version="1.0" encoding="utf-8"?>
<device-admin>
<uses-policies>
<disable-keyguard-features/>
</uses-policies>
</device-admin>
In AppActivity.java calling startLockTask() and stopLockTask()
private ComponentName mAdminComponentName;
private DevicePolicyManager mDevicePolicyManager;
...
... onCreate(){
// Retrieve Device Policy Manager so that we can check whether we can
// lock to screen later
mAdminComponentName = new ComponentName(this,AppAdminReceiver.class);
mDevicePolicyManager = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
if(mDevicePolicyManager.isDeviceOwnerApp(getPackageName())){
// App is whitelisted
setDefaultCosuPolicies(true);
}
else {
// did you provision the app using <adb shell dpm set-device-owner ...> ?
}
}
... onStart(){
// Consider locking your app here or by some other mechanism
// Active Manager is supported on Android M
if(mDevicePolicyManager.isLockTaskPermitted(this.getPackageName())){
ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
if (am.getLockTaskModeState() == ActivityManager.LOCK_TASK_MODE_NONE) {
setDefaultCosuPolicies(true);
startLockTask();
}
}
}
... unlockApp(){
ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
if (am.getLockTaskModeState() == ActivityManager.LOCK_TASK_MODE_LOCKED) {
stopLockTask();
}
setDefaultCosuPolicies(false);
}
private void setDefaultCosuPolicies(boolean active){
// Set user restrictions
setUserRestriction(UserManager.DISALLOW_SAFE_BOOT, active);
setUserRestriction(UserManager.DISALLOW_FACTORY_RESET, active);
setUserRestriction(UserManager.DISALLOW_ADD_USER, active);
setUserRestriction(UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA, active);
setUserRestriction(UserManager.DISALLOW_ADJUST_VOLUME, active);
// Disable keyguard and status bar
mDevicePolicyManager.setKeyguardDisabled(mAdminComponentName, active);
mDevicePolicyManager.setStatusBarDisabled(mAdminComponentName, active);
// Enable STAY_ON_WHILE_PLUGGED_IN
enableStayOnWhilePluggedIn(active);
// Set system update policy
if (active){
mDevicePolicyManager.setSystemUpdatePolicy(mAdminComponentName,SystemUpdatePolicy.createWindowedInstallPolicy(60, 120));
} else {
mDevicePolicyManager.setSystemUpdatePolicy(mAdminComponentName,null);
}
// set this Activity as a lock task package
mDevicePolicyManager.setLockTaskPackages(mAdminComponentName,active ? new String[]{getPackageName()} : new String[]{});
IntentFilter intentFilter = new IntentFilter(Intent.ACTION_MAIN);
intentFilter.addCategory(Intent.CATEGORY_HOME);
intentFilter.addCategory(Intent.CATEGORY_DEFAULT);
if (active) {
// set Cosu activity as home intent receiver so that it is started
// on reboot
mDevicePolicyManager.addPersistentPreferredActivity(mAdminComponentName, intentFilter, new ComponentName(getPackageName(), AppActivity.class.getName()));
} else {
mDevicePolicyManager.clearPackagePersistentPreferredActivities(mAdminComponentName, getPackageName());
}
}
private void setUserRestriction(String restriction, boolean disallow){
if (disallow) {
mDevicePolicyManager.addUserRestriction(mAdminComponentName,restriction);
} else {
mDevicePolicyManager.clearUserRestriction(mAdminComponentName,restriction);
}
}
private void enableStayOnWhilePluggedIn(boolean enabled){
if (enabled) {
mDevicePolicyManager.setGlobalSetting(mAdminComponentName,Settings.Global.STAY_ON_WHILE_PLUGGED_IN,Integer.toString(BatteryManager.BATTERY_PLUGGED_AC| BatteryManager.BATTERY_PLUGGED_USB| BatteryManager.BATTERY_PLUGGED_WIRELESS));
} else {
mDevicePolicyManager.setGlobalSetting(mAdminComponentName,Settings.Global.STAY_ON_WHILE_PLUGGED_IN,"0");
}
}
}
Similar tutorial on setting up kiosk mode
android:lockTaskMode="if_whitelisted"
add this line on your activity in AndroidManifest file
<activity android:name=".MainActivity"
android:exported="true"
android:lockTaskMode="if_whitelisted">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

UnsatisfiedLinkError when calling Native code build with Android NDK

I have a button in an simple android app that I am using to call a native function. The button in main activity calls a function of HADriver.java. This function in turn calls on a JNI function in DriverAdapter.cpp. That JNI function then in turn calls on a native function in Driver.cpp. The following are the parts of each of those files that comes into play when the button testCout is clicked in MainActivity.
Button in MainActivity:
private HADriver driver = new HADriver();
....
testCout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
driver.testCout();
}
});
HADriver.java
package com.ihearhtpi;
public class HADriver {
static {
System.loadLibrary("gnustl_shared");
System.loadLibrary("driveradapter");
}
public native void testCout();
}
DriverAdapter.cpp
#include <jni.h>
#include <string.h>
#include <android/log.h>
#include <Driver/driver.h>
#define DEBUG_TAG "NativeCalls"
void Java_com_ihearhtpi_HADriver_testCout(JNIEnv * env, jobject thiz)
{
Driver* driver = new Driver();
driver->testCoutFunc();
__android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "NDK:LC: [%s]", "Testing... this best effing work!");
delete driver;
}
Driver.cpp
#include <Driver/driver.h>
#include <iostream>
...
//bunch of custom irrelevant functions and includes
Driver::Driver()
{
CustomFuncs::initBitMasks(); // call global namespace function of CustomFuncs
}
void Driver::testCoutFunc()
{
std::cout << "These are the droids you're looking for." << std::endl;
}
However, I keep getting the following error and my app crashes each time I click the testCout button.
04-13 02:22:46.130: W/dalvikvm(17228): No implementation found for native Lcom/ihearhtpi/HADriver;.testCout:()V
04-13 02:22:46.130: D/AndroidRuntime(17228): Shutting down VM
04-13 02:22:46.130: W/dalvikvm(17228): threadid=1: thread exiting with uncaught exception (group=0x40e0c300)
04-13 02:22:46.130: E/AndroidRuntime(17228): FATAL EXCEPTION: main
04-13 02:22:46.130: E/AndroidRuntime(17228): java.lang.UnsatisfiedLinkError: Native method not found: com.ihearhtpi.HADriver.testCout:()V
04-13 02:22:46.130: E/AndroidRuntime(17228): at com.ihearhtpi.HADriver.testCout(Native Method)
What gives? I can't figure out why this native call is not working!
Apparently in my JNI file I needed to wrap all functions with
#ifdef __cplusplus
extern "C" {
#endif
...JNI functions here...
#ifdef __cplusplus
}
#endif
After this, everything is working fine
try not calling testCout through driver. try to make a wrapper function so something like driver.testCoutHelper(); in the onclick.
then in testCoutHelper() (within HADriver.java) can you call testCout(); Lets try this first and tell me the results;

Resources