I have a command line application (also it's source code in C language). I want to use it in my first android application.
Here are my questions:
I have a compiled executable for mac. Should I recompile it for android? If yes, how?
Is it possible to have a two way communication with an executable in shell in Java/Android?
I'm looking for something like:
Runtime.getRuntime().exec(cmd);
After a lot of googling, that line of code was only thing that I found. But I don't want to close the executable after each call (it communicates with a server on the internet). Here is what I want to do:
1. Open shell executable
2. write AAA
3. read line
4. write BBB
5. read line
6. write CCC
...
98. write XYZ
99. read line
100. close executable
Note that there may be nothing to read, and it shouldn't wait for it.
Yes, you need recompile it with NDK. How: you can find more information on official Android site, where examples also present.
Yes its also possible to do that, and there are two ways exist:
a) JNI. You need to write a wrapper for for you function, and compile a shared library
b) You can compile executable file, run it and communicate via input/output stream. Something like:
try {
Process pb = Runtime.getRuntime().exec(cmd);
String line;
BufferedReader input = new BufferedReader(new InputStreamReader(pb.getInputStream()));
while ((line = input.readLine()) != null) {
System.out.println("Input from your C/C++ app: " + line);
}
input.close();
} catch (IOException e) {
e.printStackTrace();
}
Related
I am making an android app which can run c, c++ and java programs. The app stores the respective files in a folder and is made to execute with the following code. Whenever I click on compile button it shows an IO Exception saying "error=13 permission denied".
try {
p = Runtime.getRuntime().exec(path + "/PocketIDE/JavaPrograms/"+ filename);
BufferedReader reader = new BufferedReader(
new InputStreamReader(p.getInputStream()));
String line = "";
while ((line = reader.readLine()) != null) {
output2.append(line).append("\n");
p.waitFor();
}
String response = output2.toString();
output.setText(response);
} catch (IOException | InterruptedException e) {
output.setText(e.toString());
e.printStackTrace();
}
Is the above method the correct way to execute the program? or do I need to change the code?
p = Runtime.getRuntime().exec(path + "/PocketIDE/JavaPrograms/"+ filename);
You shouldn't run arbitrary Java code with the runtime that controls the execution of your app. This opens a massive security flaw, so Android disallows it. Instead, you should find a way to execute the Java code in its own environment and runtime.
The statement in your code can be used to execute other programs, but it is not necessarily a good idea.
The exec method internally forks the application`s process and creates a new one, which immediately executes the system command you give it.
From the path in your code I assume that you try to execute a binary executable, which is not allowed anymore by Android since API level 28:
Untrusted apps that target Android 10 cannot invoke exec() on files within the app's home directory. This execution of files from the writable app home directory is a W^X violation. Apps should load only the binary code that's embedded within an app's APK file.
The only possible solution is to reduce the API level to 28 or include the binary in the APK file during packaging.
When i run runShellScript(unixCommand); i get following error: sh.exe has stoped working.
Can anyone tell what is the problem and how to solve it?
#!/bin/sh
# this assumes webserver is running on port 8080
echo "Deploy everything first"
echo "These next 3 should work..."
echo "The rest of these should fail... (nicely of course)"
echo "This should work but print debug info on the client and server"
# Now undeploy everything
String unixCommand = "sh U:\\home\\ash\\test.sh";
try {
runShellScript(unixCommand);
} catch (IOException e1) {
e1.printStackTrace();
} catch (InterruptedException e1) {
e1.printStackTrace();
}
public static void runShellScript(String unixCommand) throws IOException, InterruptedException {
ProcessBuilder processBuilder = new ProcessBuilder("bash", "-c", unixCommand);
processBuilder.redirectErrorStream(true);
Process shellProcess = processBuilder.start();
InputStream inputStream = shellProcess.getInputStream();
int consoleDisplay;
while ((consoleDisplay = inputStream.read()) != -1) {
System.out.println(consoleDisplay);
}
try {
inputStream.close();
} catch (IOException iOException) {
}
}
Two issues here.
First, it is good that Rekin asked whether you are using Cygwin, because that is highly relevant. Cygwin handles Windows drives by prepending cygdrive/, then the drive name. It also prefers Unix-style paths. So you should change your Unix command to:
String unixCommand = "sh /cygdrive/u/home/ash/test.sh";
That should execute your script successfully.
However you will next see that the output is just numbers instead of any legible text. That's because in your runShellScript, you are using the lowest level read() method, and are reading the process output one byte at a time, then printing each byte, each on its own separate line. At the very least, you should use a higher level stream, like DataInputStream - or basically any class with a read* method obtaining Strings instead of bytes. Even better, use a Reader subclass like BufferedReader, since this is the modern approach since JDK 1.1. Once you do that, you should see the output.
Two issues here.
First, it is good that Rekin asked whether you are using Cygwin, because that is highly relevant. Cygwin handles Windows drives by prepending cygdrive/, then the drive name. It also prefers Unix-style paths. So you should change your Unix command to:
String unixCommand = "sh /cygdrive/u/home/ash/test.sh";
That should execute your script successfully.
However you will next see that the output is just numbers instead of any legible text. That's because in your runShellScript, you are using the lowest level read() method, and are reading the process output one byte at a time, then printing each byte, each on its own separate line. At the very least, you should use a higher level stream, like DataInputStream - or basically any class with a read*-method obtaining Strings instead of bytes. Even better, use a Reader subclass like BufferedReader, since this is the modern approach since JDK 1.1. Once you do that, you should see the output.
I am trying to execute an executable file and a perl script from within a java program. I have found many topics similar to this but most of them refer to windows. I know java is platform independent and it should work anyways but it doesn't. The solution I have tried already is the one based on the java Runtime and it's exec method. It works just fine on windows but since I'm porting my program on linux I need to adapt it. As I said I need to execute an executable file that I have compiled and was written in c++ which it looks like it's working but it finishes executing with an exit value of 1. I have no idea what it means but on windows it exits with 0 and that's how it should be on linux as well (?!?!). The pearl script on the other hand does not start at all. I use the command "perl script.pl" and it exits with a value of 255. Needless to say, it doesn't do what it's supposed to.
Does anybody know another way to execute these files? Or maybe where I am wrong with my implementation?
here's the code if you want to take a look at it:
This is the one for the perl script
public static void main(String[] args){
System.out.println("Starting");
try{
String[] cmd = {"perl", "cloc-1.53.pl"};
Process pr = Runtime.getRuntime().exec(cmd);
BufferedReader input = new BufferedReader(new InputStreamReader(pr.getInputStream()));
String line=null;
while((line=input.readLine()) != null) {
System.out.println(line);
}
int exitVal = pr.waitFor();
System.out.println("Exit code: " + exitVal);
} catch (Throwable t){
t.printStackTrace();
}
}
For the compiled file I change this:
String[] cmd = {"perl", "cloc-1.53.pl"};
with:
String cmd = "./UCC";
There should be no differece in starting processes on windows and linux.
Good article http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html
Its for the old way but gives good insight.
Article converting to the new way:
From Runtime.exec() to ProcessBuilder
I want to execute a external .exe program from within java. The .exe is a CLI application which takes input in runtime( scanf() ) and outputs depending on the input. I can call the program to execute from java using
Process p = Runtime.getRuntime().exec("cmd /c start a.exe");
instead of
Process p = Runtime.getRuntime().exec("cmd /c start a.exe");
But I think it is also possible to call a program from within java. I have my whole program written in C++ just need a GUI which is written in java. There are a few things to notice:=
1) The communication with the .exe should be runtime (not through main(args) )
2) The java program should take the outputs and store in some variable / panel to use for future
3) Program to be executed can differ ( for example user may select a .exe that doesnt take any input at all)
........So basically the java GUI will act as a RuntimeEnv
public void runEXE()
{
String s = null;
try {
Process p = Runtime.getRuntime().exec("cmd /c a.exe");
System.exit(0);
}
catch (IOException e) {
System.out.println("exception happened - here's what I know: ");
e.printStackTrace();
System.exit(-1);
}
}
I know there are a lot of questions about this topic out there. But i cant find any of them much useful.
Rather ugly little function that I use. This takes in the command to be passed to Runtime.getRuntime().exec, then saves the results into a String, and returns the String at the end. You can pick whether you only want the last line (or all output) and whether you want to save the stdout or stderr string from the process.
private static String systemResult(String cmd, boolean append, boolean useErr)
{
String result = "";
try{
// spawn the external process
//printCmd(cmd);
Process proc = Runtime.getRuntime().exec(cmd);
LineNumberReader lnr1 = new LineNumberReader(new InputStreamReader(proc.getErrorStream()));
LineNumberReader lnr2 = new LineNumberReader(new InputStreamReader(proc.getInputStream()));
String line;
int done = 0;
while(lnr1 != null || lnr2 != null){
try{
if(lnr1.ready()){
if((line = lnr1.readLine()) != null){
//System.err.println("A:" +line);
if(useErr){
if(append) result = result + line + "\n";
else result = line;
}
}
}else if(done == 1){
done = 2;
}
}catch(Exception e1){
try{ lnr1.close(); }catch(Exception e2){}
lnr1 = null;
}
try{
if(lnr2.ready()){
if((line = lnr2.readLine()) != null){
//System.err.println("====>Result: " + line);
if(!useErr){
if(append) result = result + line + "\n";
else result = line;
}
}
}else if(done == 2){
break;
}
}catch(Exception e1){
try{ lnr2.close(); }catch(Exception e2){}
lnr2 = null;
}
try{
proc.exitValue();
done = 1;
}catch(IllegalThreadStateException itsa){}
}
if(lnr1 != null) lnr1.close();
if(lnr2 != null) lnr2.close();
try{
proc.waitFor();
}catch(Exception ioe){
}finally{
try{
proc.getErrorStream().close();
proc.getInputStream().close();
proc.getOutputStream().close();
}catch(Exception e){}
proc = null;
}
}catch(Exception ioe){
}
return result;
}
You could use JNI as #linuxuser27 suggests, or you could use SWIG which helps to make the process of communicating from Java --> C++ a little less painful.
Google Protocol Buffers would be a good option for Java/C++ interoperability.
Protocol buffers are Google's
language-neutral, platform-neutral,
extensible mechanism for serializing
structured data – think XML, but
smaller, faster, and simpler. You
define how you want your data to be
structured once, then you can use
special generated source code to
easily write and read your structured
data to and from a variety of data
streams and using a variety of
languages – Java, C++, or Python.
I would look at JNI and use some type of IPC to communicate with the C++ project.
Update:
JNI is a way for Java to interface with the underlying native environment the JRE is running on. This method would require you to create a DLL that is loaded into the JRE when your Java program starts. This JNI DLL would then contain a method which could be called from within your Java program that would pass data into the JNI DLL that could then communicate to the C++ project via a named pipe or shared memory.
The named piped would be create using the CreateNamedPipe Win32 API. Within the JNI DLL you would most likely create a server and in the C++ project you would create the client. Note that the server example is multi-threaded but can easily be converted to a single thread model for simplicity.
Note that this is not a simple task. The other answers offer some approaches that are easier, JNA and passing data to the C++ project via stdin.
A couple of things:
First and foremost, if you haven't done so, read this critically important article: When Runtime.exec won't
Next, there are several ways for Java to communicate to other applications, and probably the easiest is via standard input and output streams. Have you tried using these?
Next there's JNI and the easier JNA, but you stated that your C++ program is running via CLI which suggests to me that you have a .NET dll, not a true Windows dll. Is this so? If so, it would make communication between Java and C++ more difficult.
You can execute .exe programs directly. You just need to supply the full path. Relative path will also be ok.
You can use pipes to interface with an external process: Sending Input to a Command, Reading Output from a Command
The solution can be found here
Problem getting output and passing input to a executing process running under java
From my java project I want to run an external .bat file in another thread. For this purpose I use the following method:
private void posAppRunner(final String path[], final Class targetClass) {
new Thread(new Runnable() {
public void run() {
try {
String line;
Process p = Runtime.getRuntime().exec(path);
BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
while ((line = input.readLine()) != null) {
System.out.println(line);
}
input.close();
} catch (IOException e) {
LogFactory.getLog(targetClass).warn("Error when starting a PosApplication: " + e.getMessage());
}
}
}).start();
I run the following .bat file:
call chdir %~dp0
start java <_some_arguments>
So when I do it locally from IntelliJ IDEA it works correct - a cmd process appears, after that a java process appears and after that the cmd process disappears.
But when I run my java project with this method through ANT under TeamCity windows service, only cmd process appears and nothing happens after. Java process that must be started from the bat file doesn't appear. It looks like I don't read the process output but I do!
Could you expain me, how to overcome this situation?
I believe that the problem is in current working directory. I am not so familiar with bat files and do not remember by heart what does %~dp0 mean. Anyway as the first attempt try to modify your batch file to contain the hard coded path. I believe that this will work.
In this case decide what is better for you: discover the path in java code and then pass it to batch file, generate batch file on the fly, so that it contains all parameters hard coded or debug the script. for example you can remove the start java <_some_arguments> and put
echo %~dp0 > c:\temp\batlog.log
this will print the parameter to log file. Now run it as service and see what does the log file contain. Probably you will see the problem immediately.