I have been trying to get this right, but with no success.
I am executing a command to get a ping result, then store it in a variable, but when I show the variable in a TOAST it always displays "NULL".
Here's my code:
fun pingthis(): String? {
try
{
//execute the command
val pingprocess: Process = Runtime.getRuntime().exec("/system/bin/ping -c 1 -s 64 $host")
pingprocess.waitFor()
val bufferedReader = BufferedReader(InputStreamReader(pingprocess.inputStream))
//read the result
val logger: StringBuilder = StringBuilder()
var line: String? = ""
while (line != null) {
line = bufferedReader.readLine()
logger.append(line + "\n")
}
pingResult = logger.toString()
} catch (e: IOException) {}
return pingResult
}
as you can see, "pingResult" always ALWAYS returns NULL. I don't know why. What have I done wrong?
I should've probably guessed this on my own but I was not focused at all lol; It took me three days to know that I am pinging without internet permissions on my app. Which is, if you ask me, very stupid.
The code above works very well for executing ping command and parsing the output, but I always got null because the ping command won't even run if I had no internet permissions.
I added network permissions to my Android Manifest and it's working perfectly.
Also thanks to assylias's comment above. You shouldn't do "process.waitFor()' then read the output, because the process would already be terminated.
Related
I am changing the way our application works to use retrofit instead of just OkHTTP
The way it used to work is we would send the request, retrieve the body as an input stream and read all bytes into a string.
After that we would parse the body using gson.
The problem is that the server seems to have a problem with the configuration (which I am told is on the list of things to fix but will take a long time) so for example it may return 400 bytes of data, but will send the message that the bytes are actually 402.
The way we currently handle it is by catching the EOF exception and ignoring it, and then parsing the returned string normally.
right now I use the following request to get the entities I want
#GET("/services/v1/entities")
suspend fun getEntities() : List<ServerEntity>
which , when there is no error, works correctly
the solutions I've found so far are either
a) use the following code to retry all requests until I do not get an EOF exception:
internal suspend fun <T> tryTimes(times: Int = 3, func: suspend () -> T): T {
var tries = times.coerceAtLeast(2)
try {
var lastException: EOFException? = null
while (tries > 0) {
try {
return func.invoke()
} catch (eof: EOFException) {
lastException = eof
tries--
}
}
throw lastException!!
} finally {
log.d("DM", "tried request ${times.coerceAtLeast(2) - tries} times")
}
}
which most of the time logs either 0 or 1 tries
or change all my requests to
#GET("/services/v1/entities")
suspend fun getEntities() : ResponseBody
and parse the stream manually ( ResponseBody may be incorrect but you can understand what I mean)
is there a way to use my original function and make retrofit know that in the case of an EOF exception it should resume instead of stopping?
I have been working on this small project in college about changing the default DNS of wifi network to a custom DNS like Google, OpenDNS, Metacert, etc.
I know I have to write a shellscript inside the app's code that would edit the hosts file in the filesystem.
The problem is I have no idea where to start from. I have researched on google for some time and I couldn't figure anything.
If anyone knows about it, please guide me. Please tell me the name of the file to be edited, its location, what commands are required and how to run those commands' combination as a shellscript on a click of a button on the UI of app.
EDIT : I'm stuck only at this. Any help will be greatly appreciated.
I'm not sure about which files you would have to edit but this should give you the tools you need to do that.
The first thing you need to do is root the phone if you haven't already. If it's not rooted, you'll run into an issue like: Working Directory : null environment when running Process.Builder on android
There are a lot of guides available for that online. Install SuperSU as well. In order to run shell commands or scripts you should look at the ProcessBuilder class in Android:
http://developer.android.com/reference/java/lang/ProcessBuilder.html
I've given some sample code below to help you along the way. You could execute this in an OnClick() for a button.
/**
* Runs the shell command.
*
* #param command an array of Strings. command[0] contains the name of the
* shell command. command[1]... contains parameters.
*
* #return the text outputted by the command to stderr or stdout
*/
String runCmd(String[] command, boolean readOutput,
boolean waitForExit) {
ProcessBuilder probuilder = new ProcessBuilder()
.command(command)
.redirectErrorStream(true);
String output = "";
Process process;
// Log.d("MyShellCommand", "Executing " + command[0]);
try {
process = probuilder.start();
} catch(IOException e) {
return e.getMessage();
}
if (readOutput) {
InputStream is = process.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line;
try {
while ((line = br.readLine()) != null) {
// Log.d("MyShellCommand", "Read a line: " + line);
output += line + "\n";
}
} catch(IOException e) {
output = e.getMessage();
}
}
I've implemented a service that listens to commands issued through ADB. An example of a command sent through ADB could look like this:
adb shell am startservice -a com.testandroid.SEND_SMS -e number 123123123 -e message "åäö"
Now, the problem here is that the encoding of the string "åäö" seems to mess up. If I take that string extras and immediately output it to the log, I get a square "[]", unknown character. If I send this message I get chinese characters in the messages app. As long as I stick to non-umlaut characters (ASCII I guess), everything works fine.
I'm using Windows 7 and the command line for this. I have not touched the encoding of the command line and I've tried to process the extras string by getting the byte characters, passing in UTF-8 as an encoding argument, then creating a new String passing in UTF-8 as an encoding argument there as well. No dice, though.
The values of the bytes, when using getBytes() are å: -27, ä: -92, ö: -74
How do I get this to play nice so I can make use of at least the umlauts?
All of this works perfectly fine in Linux.
i ran into the same issue, but finally i got it work!
if you use for example C#, you have to do it like the following example:
02.12.2019
According to the protocol.txt, the ADB-Protocol supports "smart-sockets". Those sockets can be used to do all the stuff, the ADB-Client inside the adb.exe does. For example if you want upload an file, you have to request such an "smart-socket". After that, you have to follow the protocol assigned to the service (for an service overview see SERVICE.txt) as described, for example, in the SYNC.txt.
13.10.2014
public static List<string> ExecuteBG(string exe, string args, int timeOut = -1)
{
if (File.Exists(exe) || exe == "cmd.exe")
{
ProcessStartInfo StartInfo = new ProcessStartInfo();
StartInfo.FileName = exe;
StartInfo.Arguments = Encoding.Default.GetString(Encoding.UTF8.GetBytes(args));
StartInfo.CreateNoWindow = true;
StartInfo.UseShellExecute = false;
StartInfo.RedirectStandardError = true;
StartInfo.RedirectStandardOutput = true;
StartInfo.StandardErrorEncoding = Encoding.UTF8;
StartInfo.StandardOutputEncoding = Encoding.UTF8;
AutoResetEvent errorWaitHandle = new AutoResetEvent(false);
AutoResetEvent outputWaitHandle = new AutoResetEvent(false);
List<string> response = new List<string>();
Process proc = new Process();
proc.StartInfo = StartInfo;
proc.ErrorDataReceived += (s, e) =>
{
if (String.IsNullOrEmpty(e.Data))
{
errorWaitHandle.Set();
}
else
{
response.Add(e.Data);
}
};
proc.OutputDataReceived += (s, e) =>
{
if (String.IsNullOrEmpty(e.Data))
{
outputWaitHandle.Set();
}
else
{
response.Add(e.Data);
}
};
proc.Start();
proc.BeginErrorReadLine();
proc.BeginOutputReadLine();
proc.WaitForExit(timeOut);
errorWaitHandle.WaitOne(timeOut);
outputWaitHandle.WaitOne(timeOut);
return response;
}
return new List<string>();
}
Really important is this part "StartInfo.Arguments = Encoding.Default.GetString(Encoding.UTF8.GetBytes(args));", here we convert the UTF8 string into the Windows "default" charset which is known by cmd. So we send a "destroyed" "default" encoded string to cmd and the Android shell will convert it back to UTF8. So we have the "umlauts" like "üöäÜÖÄàè etc.".
Hope this helps someone.
PS: If u need a working "Framework" which supports UTF8 push/pull for files/folders also have a look at my AndroidCtrl.dll it's C# .NET4 written.
Regards,
Sebastian
Concluding, either the problem is situated in cmd.exe or adb.exe. Until either one or both are updated to be more compliant with eachother I will sadly not be able to make use of this for the time being.
I've been trying to build a Terminal Emulator for Android. Being pretty new to this, my idea was to execute each command and store the output in a file, whose contents would be displayed after each execution.
Pseudo Code :
public Boolean execCommands(String command) {
try {
rt = Runtime.getRuntime();
process = rt.exec("su");
DataOutputStream os = new DataOutputStream(process.getOutputStream());
os.writeBytes("echo $ \""+command+ "\" >> /sdcard/Android/data/terminalemulatorlog.txt\n\n\n");
/**** Note : String command = (EditText)findViewById(R.id.command).getText().toString(); ****/
os.flush();
os.writeBytes("exit\n");
os.flush();
process.waitFor();
}
// Error Handling
displayOutput(); //Loads and displays the Text File (/sdcard/Android/data/terminalemulatorlog.txt)
return true;
}
This piece of code works except for a few special commands (Eg. 'clear').
But what I'm more concerned about are the following problems :
Each time a command is to be executed, I end up seeking SuperUser permissions (second line of code). And I'd like to do away with this.
In cases when the user enters one command followed by another,
Such as :
cd /sdcard
touch File.txt
The File.txt is created in '/' and not in '/sdcard'. As of now to avoid this, I'm keeping a track of all the 'cd' commands to figure out what the present working directory is. And I'm hoping that there is a better way around this.
I'd be grateful if someone could help me out here.
Not sure if you are still needing this or not, but here is how I am issuing multiple commands at one time and not using "su" to have them run.
try {
String[] commands = {
"dumpstate > /sdcard/LogFiles/dumpstate.txt",
"dumpsys > /sdcard/LogFiles/dumpsys.txt",
"logcat -d > /sdcard/LogFiles/log.txt",
"cat /sdcard/LogFiles/dumpstate.txt /sdcard/LogFiles/dumpsys.txt /sdcard/LogFiles/log.txt > /sdcard/LogFiles/bugreport.rtf" };
Process p = Runtime.getRuntime().exec("/system/bin/sh -");
DataOutputStream os = new DataOutputStream(p.getOutputStream());
for (String tmpCmd : commands) {
os.writeBytes(tmpCmd + "\n");
}
} catch (IOException e) {
e.printStackTrace();
}
This a bit late but here a few ways of doing this.
1)
Instead of using su as a starting point use /system/bin/sh.
and after calling
rt.exec("/system/bin/sh");
You should hold onto the Output Stream and Input Stream to give further commands.
After you issued a command you should echo a magic line like "---EOF---" and stop reading input after reading that line. If you don't have this you'll end up with the read function from the InputStream blocking.
2) Pipe the data to a native process you've written that simply moves the data on to your Android Application with a terminating character or string attached to the end.
I am not entirely sure how to do this, but it is essentially the same as the previous method just relies on you native application as a middle man.
This will get you close to a functioning "Terminal Emulator".
3)If you wan't a true Ternimal Emulator then there's no other way to do it than : using a native application that opens a connection to a psuedoterminal.
Here's some basic information of how to open a pty : link
Terminal Emulator is a open source project that uses this technique.
Have a look here
Regarding problem 1:
Each time a command is to be executed, I end up seeking SuperUser permissions (second line of code). And I'd like to do away with this.
Thanks to Xonar's suggestion from another answer:
After you issued a command you should echo a magic line like "---EOF---" and stop reading input after reading that line.
Solution in Kotlin:
private lateinit var suProcess: Process
private lateinit var outputStream: DataOutputStream
private fun getSu(): Boolean {
return try {
suProcess = Runtime.getRuntime().exec("su")
outputStream = DataOutputStream(suProcess.outputStream)
true
} catch (e: Exception) {
e.printStackTrace()
false
}
}
private fun sudo(command: String): List<String>? {
return try {
outputStream.writeBytes("$command\n")
outputStream.flush()
outputStream.writeBytes("echo ---EOF---\n")
outputStream.flush()
val reader = suProcess.inputStream.bufferedReader()
val result = mutableListOf<String>()
while (true) {
val line = reader.readLine()
if (line == "---EOF---") break
result += line
}
result
} catch (e: Exception) {
e.printStackTrace()
null
}
}
private fun exitTerminal() {
try {
outputStream.writeBytes("exit\n")
outputStream.flush()
suProcess.waitFor()
} catch (e: Exception) {
e.printStackTrace()
} finally {
outputStream.close()
}
}
//Activity method
override fun onDestroy() {
super.onDestroy()
exitTerminal()
}
i am trying to do login application which takes id and password..when i click on logi button then it will connect to our local server by JSON..with the specified URL..the code is..
var loginReq = Titanium.Network.createHTTPClient();
loginReq.onload = function()
{
var json = this.responseText; alert(json);
var response = JSON.parse(json);
if (response.data.status == "success")
{ alert("Welcome ");
}
else
{ alert(response.data.status);
}
};
loginReq.onerror = function(event)
{
alert(event.toSource());
//alert("Network error");
};
loginBtn.addEventListener('click',function(e)
{ if (username.value != '' && password.value != '')
{
var url = 'our local url action=login&id='+username.value+'&pwd='+password.value;
loginReq.open("POST",url);
loginReq.send();
}
else
{
alert("Username/Password are required");
}
});
Here it is not connecting our URl..so it is entering into loginReq.onerror function...instead of loginReq.onload function..why it is throwing run time error.. The same code working fine with Iphone..
The Run Time Error is..
TypeError:Cannot call property toSource in object{'source':[Ti.Network.HttpClient],specified url} is not a function,it is a object.
This is wat the error..please let me Know...
Apparently the toSource() function does not exist in android, as it is an object. Try debugging and see what the object event contains.
You could do that by adding a line above the alert line, and adding a debug line to it.
Look in debug mode and see all variables
"toSource()" is not a documented function for either platform, and I also do not see it in the source for Titanium Mobile. If you aren't getting the error on iOS, I'm guessing it is because the error handler isn't getting called. Perhaps your emulator or device does not have internet access, whereas your iOS simulator or device does?
Regardless, error handling in the HTTPClient normally looks something like this:
loginReq.onerror = function(e)
{
Ti.API.info("ERROR " + e.error);
alert(e.error);
};