Android

Hosting an executable within Android application

Размещение исполняемого файла в приложении Android

Я работаю над приложением для Android, которое зависит от двоичного файла ELF: наш Java-код взаимодействует с этим двоичным файлом для выполнения задач. Эта среда выполнения должна запускаться и завершаться при запуске приложения и выходе из приложения / по требованию.

Вопросы:


  1. Я предполагаю, что мы сможем выполнить этот двоичный файл, используя Runtime.exec() API. Есть ли какие-либо ограничения относительно того, куда мне нужно поместить мою библиотеку в структуре папок? Как системная среда выполнения найдет этот исполняемый файл? Есть ли какая-то настройка пути к классу?


  2. Поскольку приложение зависит от этой среды выполнения, я думал обернуть его вокруг службы, чтобы его можно было запускать или останавливать по мере необходимости. Каков наилучший способ обработки таких исполняемых файлов в Android Project?


  3. Каковы другие альтернативы, при условии, что у меня нет исходного кода для этого исполняемого файла?


Пожалуйста, дайте совет.

Спасибо.

Переведено автоматически
Ответ 1

1) Нет, не должно быть никаких ограничений, кроме тех, которые обеспечивают доступ к системным файлам и, следовательно, требуют root. Лучшим местом было бы сразу перейти в /data/data /[your_package_name], чтобы избежать загрязнения в другом месте.

2) Очень подробное обсуждение компиляции с использованием собственных библиотек можно найти здесь: http://www.aton.com/android-native-libraries-for-java-applications / . Другой вариант - кросс-компилятор для arm (вот тот, который используется для компиляции ядра, он бесплатный: http://www.codesourcery.com/sgpp/lite/arm ). Если вы планируете поддерживать службу, которая выполняет ваш cammand, имейте в виду, что службы могут быть остановлены и перезапущены Android в любой момент.

3) Теперь, если у вас нет исходного кода, я надеюсь, что ваш файл, по крайней мере, скомпилирован как исполняемый файл arm. Если нет, я не понимаю, как вы вообще могли бы его запустить.


Вы запустите файл, выполнив следующие команды в вашем классе java:

String myExec = "/data/data/APPNAME/FILENAME";
Process process = Runtime.getRuntime().exec(myExec);
DataOutputStream os = new DataOutputStream(process.getOutputStream());
DataInputStream osRes = new DataInputStream(process.getInputStream());

Я ничего не знаю о вашем исполняемом файле, поэтому вам может понадобиться, а может и не понадобиться на самом деле получать InputStream и OutputStream.


Я предполагаю, что о запуске adb для отправки двоичного файла не может быть и речи, поэтому я искал аккуратный способ его упаковки. Я нашел отличный пост о включении исполняемого файла в ваше приложение. Проверьте это здесь: http://gimite.net/en/index.php?Run%20native%20executable%20in%20Android%20App

Важная часть - это (выделено мной):


Из приложения Android Java, используя assets папку



  • Включите двоичный файл в папку assets.

  • ИспользуйтеgetAssets().open(FILENAME), чтобы получить InputStream.

  • Запишите его в /data/data/APPNAME (например, /data/data/net.gimite.nativeexe), где у вашего приложения есть доступ для записи файлов, и сделайте его исполняемым.

  • Запустите /system/bin/chmod 744 /data/data/APPNAME/FILENAME, используя приведенный выше код.

  • Запустите свой исполняемый файл, используя приведенный выше код.


В сообщении используется assets папка вместо raw папки, которую Android предлагает для статических файлов:


Совет: Если вы хотите сохранить статический файл в своем приложении во время компиляции, сохраните файл в каталоге project res / raw / . Вы можете открыть его с помощью openRawResource(), передав идентификатор R.raw. resource . Этот метод возвращает InputStream, который вы можете использовать для чтения файла (но вы не можете выполнить запись в исходный файл).


Чтобы получить доступ к папке с данными, вы можете следовать инструкциям здесь: http://developer.android.com/guide/topics/data/data-storage.html#filesInternal Также есть File#setExecutable(boolean); метод, который должен работать вместо команды командной строки.

Итак, собрав все воедино, я бы попробовал:

InputStream ins = context.getResources().openRawResource (R.raw.FILENAME)
byte[] buffer = new byte[ins.available()];
ins.read(buffer);
ins.close();
FileOutputStream fos = context.openFileOutput(FILENAME, Context.MODE_PRIVATE);
fos.write(buffer);
fos.close();


File file = context.getFileStreamPath (FILENAME);
file.setExecutable(true);

Конечно, все это следует выполнять только один раз после установки. Вы можете выполнить быструю проверку внутри onCreate() или что-то еще, что проверяет наличие файла и выполняет все эти команды, если файла там нет.

Дайте мне знать, если это сработает. Удачи!

Ответ 2

Вот полное руководство по упаковке и запуску исполняемого файла. Я основал его на том, что нашел здесь, и других ссылках, а также на своих собственных пробах и ошибках.

1.) В вашем проекте SDK поместите исполняемый файл в папку / assets

2.) Программно получаем строку этого каталога файлов (/data /данные /имя__аппликации/файлы) следующим образом

String appFileDirectory = getFilesDir().getPath();
String executableFilePath = appFileDirectory + "/executable_file";

3.) В Java-коде проекта вашего приложения: скопируйте исполняемый файл из папки / assets в подпапку "files" вашего приложения (обычно /data / данные / имя__приложения / файлы) с помощью функции, подобной этой:

private void copyAssets(String filename) {

AssetManager assetManager = getAssets();

InputStream in = null;
OutputStream out = null;
Log.d(TAG, "Attempting to copy this file: " + filename); // + " to: " + assetCopyDestination);

try {
in = assetManager.open(filename);
Log.d(TAG, "outDir: " + appFileDirectory);
File outFile = new File(appFileDirectory, filename);
out = new FileOutputStream(outFile);
copyFile(in, out);
in.close();
in = null;
out.flush();
out.close();
out = null;
} catch(IOException e) {
Log.e(TAG, "Failed to copy asset file: " + filename, e);
}

Log.d(TAG, "Copy success: " + filename);
}

4.) Измените права доступа к файлу executable_file, чтобы он действительно стал исполняемым. Сделайте это с помощью вызовов Java:

File execFile = new File(executableFilePath);
execFile.setExecutable(true);

5.) Запустите файл следующим образом:

Process process = Runtime.getRuntime().exec(executableFilePath);

Обратите внимание, что для любых файлов, упомянутых здесь (таких как входные и выходные файлы), должны быть сконструированы полные строки пути. Это связано с тем, что это отдельный созданный процесс, и он не имеет понятия о том, что такое "pwd".

Если вы хотите прочитать стандартный вывод команды, вы можете это сделать, но пока у меня это работает только для системных команд (например, "ls"), а не для исполняемого файла:

BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream()));
int read;
char[] buffer = new char[4096];
StringBuffer output = new StringBuffer();
while ((read = reader.read(buffer)) > 0) {
output.append(buffer, 0, read);
}
reader.close();
process.waitFor();

Log.d(ТЕГ, "output: " + вывод.toString());

Ответ 3

Выполнение двоичного файла, начиная с Android 10, возможно только из папки, доступной только для чтения. Это означает, что вы должны упаковать двоичный файл в свое приложение. Android doc


  1. Поместить android:extractNativeLibs="true" в AndroidManifest;

  2. Поместите свой двоичный файл в src/main/resources/lib/* каталог, где * – обозначает архитектуру процессора, например, armeabi-v7a;

  3. Используйте подобный код для выполнения:


    private fun exec(command: String, params: String): String {
    try {
    val process = ProcessBuilder()
    .directory(File(filesDir.parentFile!!, "lib"))
    .command(command, params)
    .redirectErrorStream(true)
    .start()
    val reader = BufferedReader(
    InputStreamReader(process.inputStream)
    )
    val text = reader.readText()
    reader.close()
    process.waitFor()
    return text
    } catch (e: Exception) {
    return e.message ?: "IOException"
    }
    }

Вот обсуждение с ответом от команды Android на reddit.

Ответ 4

Я сделал что-то подобное с помощью NDK. Моя стратегия заключалась в том, чтобы перекомпилировать программу с помощью NDK и написать некоторый JNI-код-оболочку, который вызывал функцию main программы.

Я не уверен, на что похож жизненный цикл кода NDK. Даже службы, которые предназначены для длительной работы, могут запускаться и останавливаться системой, когда это удобно. Вероятно, вам придется завершить работу вашего потока NDK и перезапустить его при необходимости.

java android