Runtime.exec() bug: hangs - android

First thing my app does is checking for "su" since it's necessary for the app to work. Even though it sometimes work, often after typing "killall packageName" in the terminal. I've done a simple test application and I can't get it to work every time.
Code where it happens:
String[] args = new String[] { "su" };
Log.v(TAG, "run(" + Arrays.toString(args) + ")");
FutureTask<Process> task = new FutureTask<Process>(new Callable<Process>() {
#Override
public Process call() throws Exception {
return Runtime.getRuntime().exec(args);
}
});
try {
Executors.newSingleThreadExecutor().execute(task);
return task.get(10, TimeUnit.SECONDS);
} catch (Throwable t) {
task.cancel(true);
throw new IOException("failed to start process within 10 seconds", t);
}
Complete project: https://github.com/chrulri/android_testexec
Since this app does nothing more than running exec() in the first place, I cannot close any previously opened file descriptors as mentioned in another stackoverflow question: https://stackoverflow.com/a/11317150/1145705
PS: I run Android 4.0.3 / 4.0.4 on different devices.

3c71 was right about open file descriptors. In my case, it was the AdMob SDK which caused the problems since it was sometimes (re-)loading the Ads from the web at the sime time I tried to call exec(..) leaving me hanging in a deadlock.
My solution is to fork a "su" process ONCE and reuse it for all commands and load the Ads AFTER forking that process.

To use Runtime.exec safely you should wait for the process to finish and consume the output and error streams, preferably concurrently (to prevent blocking):
http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html

Related

How to destroy a golang sub process on Android?

Recently, I am trying Android sub process with Runtime.getRuntime().exec(command) and found that I can destroy a NodeJS http server but cannot destroy a Go http server.
for node and go binary, it is available from Termux;
node http server: https://github.com/stallpool/halfbase/tree/master/nodejs/tinyserver/index.js
go http server: https://github.com/stallpool/halfbase/blob/master/golang/tinyserver/main.go
For node sub process, it can be started in an Android Service and p.waitFor(); when it is time, it can be killed by p.destroy()
However, for go sub process, it can be started but cannot be killed by p.destroy() even p. destroyForcibly(); in this article https://medium.com/honestbee-tw-engineer/gracefully-shutdown-in-go-http-server-5f5e6b83da5a , it makes sure a go server can be closed gracefully and I tried it but p.destroy() still does not work.
It is appreciated if anyone can light me a way to kill the process. thx!
just figured out a hack way; not elegant; guide me to a better solution if any!
Log.d("AppDebug", p.javaClass.getName())
// from above log
// we can know Android use "java.lang.UNIXProcess" as implementation of java.lang.Process
// to make sure the sub process is killed eventually
if (p.isAlive()) {
val klass = p.javaClass
if (klass.getName().equals("java.lang.UNIXProcess")) {
Log.d("AppDebug", "force terminate sub process ..")
try {
val f = klass.getDeclaredField("pid");
f.setAccessible(true);
val pid = f.getInt(p);
// XXX: buggy here, if getInt throw an error, the filed is exposed!
f.setAccessible(false);
android.os.Process.killProcess(pid);
Log.d("AppDebug", "force terminating done.")
} catch (e: Exception) {
Log.d("AppDebug", "force terminating failed.")
}
} else {
Log.d("AppDebug", "force terminating not supported.")
}
}
sorry for my misleading. currently I totally understand why my go server cannot be killed after I add some log about ps -ef before/after kill the process.
actually I use go run main.go to start the server; however, go run main.go will compile the code and generate a binary file in tmp folder; then it will spawn a child process (execute the binary); when I did p.destroy(), it merely kill the go process but the child server process remains there.
the correct solution is, get pid like above code; and use ps -o pid= --ppid=<pid> to get children tree and kill all processes for a cleanup.

Running ExtractDecodeEditEncodeMuxTest outside of testcase on Android

I am trying to add functionality to extract, decode, edit, encode and mux a video on Android. Therefore, I found some very useful implementation, which is part of the Android CTS ExtractDecodeEditEncodeMuxTest. Unfortunately, the code only works if it is executed as part of the testcase. I tried to execute it from a normal activity and get:
E/ExtractDecodeEditEncodeMuxTest (18781): java.lang.IllegalStateException:
Failed to stop the muxer
W/System.err(18781): java.lang.RuntimeException:
Surface frame wait timed out
W/System.err(18781): at ...OutputSurface.awaitNewImage(OutputSurface.java:216)
Any ideas, why the output surface does not receive the frames?
UPDATE:
Here are the log files for the working test case and the non-working implementation. The code for both is exactly the same. The only difference is that the working one is an AndroidTestCase and the other one is running in the application within an IntentService.
It seems that the whole thing stops extracting and decoding after about 6 frames. Any ideas?
Working Testcase Logoutput
Non-Working Log Output
morelikely you need to run it in separate thread
public static void runTest(ExtractDecodeEditEncodeMuxTest test) throws Throwable {
test.setOutputFile();
TestWrapper wrapper = new TestWrapper(test);
Thread th = new Thread(wrapper, "codec test");
th.start();
th.join();
if (wrapper.mThrowable != null) {
throw wrapper.mThrowable;
}
}
Thank's to fadden, I was able to resolve this issue. I am using an intent service now and start a thread without looper there, which works fine.
For running the code in an Android service, this means that the wrapping thread has to be started from a custom thread. Starting a thread within a thread is probably not the very best solution, but it actually solves the problem.

Viewing logcat in tablet

Is there a way to view the log on a tablet running 4.4? I've downloaded several apps like aLogCat and none of them show what my app writes out with S.o.p or Log.d. I have an intermittent bug that gives the Unfortunately appname has stopped message.Is there any way to view the log after this event without having to connect to a PC and use the adb program?
What other ways are there to get debug output? Would trapping the System.out and System.err classes get the stack trace?
Thanks,
Norm
You're focussing on tring to read out logcat, but there are better solutions for reading crash logs. My personal preference is Crashlytics, which automatically logs fatal exceptions and provides mechanisms for logging other messages.
The way all these crash reporters work, is by defining a UncaughtExceptionHandler:
Thread.setDefaultUncaughtExceptionHandler(
new MyUncaughtExceptionHandler(this));
If you prefer to use your own solution, you may want to look into using this. See this related question for more details.
Is there a way to view the log on a tablet running 4.4?
No, sorry. An app can only see its own log messages, not those from other apps. Hence, a third-party log viewer cannot see your app's messages.
Is there any way to view the log after this event without having to connect to a PC and use the adb program?
Use any standard crash management library, like ACRA, or services like Crashlytics, BugSense, etc.
The AIDE Application (Android Integrated Development Environment) allows one to develop android Apps directly on android device.
One particular feature is to read the logcat.
You can get it here https://play.google.com/store/apps/details?id=com.aide.ui
Here's the code I've put in the program. It seems to work:
// Define inner class to handle exceptions
class MyExceptionHandler implements Thread.UncaughtExceptionHandler {
public void uncaughtException(Thread t, Throwable e){
java.util.Date dt = new java.util.Date();
String fn = LogFilePathPfx + "exception_" + sdf.format(dt) + ".txt";
try{
PrintStream ps = new PrintStream( fn );
e.printStackTrace(ps);
ps.close();
System.out.println("wrote trace to " + fn);
e.printStackTrace(); // capture here also???
SaveStdOutput.stop(); // close here vs calling flush() in class
}catch(Exception x){
x.printStackTrace();
}
lastUEH.uncaughtException(t, e); // call last one Gives: "Unfortunately ... stopped" message
return; //???? what to do here
}
}
lastUEH = Thread.getDefaultUncaughtExceptionHandler(); // save previous one
Thread.setDefaultUncaughtExceptionHandler(new MyExceptionHandler());

Keep Logcat from deleting entries

In Eclipse, I notice that Logcat only retains a few dozen entries and deletes the older ones as soon as a new one come in. Is there a way to prevent this? I need my app to run for a long time and not lose any entries because my app eventually hangs or crashes after a few days, and I want to see if something in Logcat has been recorded.
I am not sure if this is the most elegant solution to the problem, but you can always increase the LogCat message size in Eclipse.
Window -> Preferences -> Android -> LogCat -> Maximum number of LogCat messages to buffer
The default is 5000, I believe. You can set it to be very high if you are planning to run your application for a long time.
i think you need to increase this show image
Here's a better solution:
Set the Default Uncaught Exception Handler. Whenever the app crashes, this will be called with the exception. Simply write a log entry saying it crashed then dump the logcat to a file. Finally, make sure you re-throw the exception to make sure the app crashes and funky things don't happen. Note: This is per thread, keep that in mind.
Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {
#Override
public void uncaughtException(Thread thread, Throwable ex) {
Log.e("TAG", "---My app crashed just now---", ex);
//TODO: Dump logcat to file
throw new RuntimeException(ex);
}
});
if you want to keep your app running for days.. its better you capture your logs from adb shell.
the common shell command would be :
logcat -c \\ to clear previous logs
logcat -v time>yourLogs.txt & \\ to capture fresh logs

Unauthorized Caller Error

I am stuck writing some code that uses reflection that calls IConnectivityManager.startLegacyVpn
The error I get is java.lang.SecurityException: Unauthorized Caller
Looking through the android source I see this is the code hanging me up:
if (Binder.getCallingUid() != Process.SYSTEM_UID) { raise the above exception }
My question is if I root my AVD and install my app in system/app will this be sufficient to get around this error?
If so, any tips on how to do this (every time I try to move my apk to the system/app folder it says the app is not installed when I click on the app icon.
Thanks!
I have the same problem, following android 4.01 open source, i see somethings like this:
public synchronized LegacyVpnInfo getLegacyVpnInfo() {
// Only system user can call this method.
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
throw new SecurityException("Unauthorized Caller");
}
return (mLegacyVpnRunner == null) ? null : mLegacyVpnRunner.getInfo();
}
Or,
// Only system user can revoke a package.
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
throw new SecurityException("Unauthorized Caller");
}
Or,
public void protect(ParcelFileDescriptor socket, String interfaze) throws Exception {
PackageManager pm = mContext.getPackageManager();
ApplicationInfo app = pm.getApplicationInfo(mPackage, 0);
if (Binder.getCallingUid() != app.uid) {
throw new SecurityException("Unauthorized Caller");
}
jniProtect(socket.getFd(), interfaze);
}
However, these block of code above is belongs to com.android.server.connectivity.Vpn
(class Vpn), which is not defined in interface IConnectivityManager.
I also find in startLegacyVpnInfo() function but i can't see anything involve exception
"Unauthorized Caller", so i wonder why startLegacyVpnInfo() function throws this exception?
Any solutions for this?
I am trying to make the same calls. So far I can confirm that rooting the device and copying the apk to /system/app does not work, it does not start under the system uid.
Also, this does not work:
Field uidField = Process.class.getDeclaredField("SYSTEM_UID");
uidField.setAccessible(true);
uidField.set(null, Process.myUid());
Those calls succeed, but they don't seem to affect the SYSTEM_UID field, the field is probably optimized out at compile time.
If you include android: sharedUserId="android.uid.system" into your manifest tag (not just the manifest), this should then run the application as system. This should now let you run the code.
As for pushing to /system/app, you need to run adb root followed by adb remount. This will now let you push to /system/app.

Categories

Resources