Unexpected different results from the same String input
Asked Answered
C

2

13

I have a BroadcastReceiver that receives input from an outside source.

This receiver must then behave as a "mouse-like" program and send input events to the system. I have Root access and permissions.

My problem is that when I send a String such as "input tap 275 410", the program behaves correctly, if I even split the string such as "input" + " tap" + " 275" + " 410", it still works...

However, when I assemble the string as the desired:

"input " + CursorSystem.command + " " + coords[0] + " " + coords[1]

Then nothing happens... while debugging, all the watched Strings are exactly the same (minus the location):

value = {char[26]@830031306976}  0 = 'i' 105 1 = 'n' 110 2 = 'p' 112 3 = 'u' 117 4 = 't' 116 5 = ' ' 32 6 = 't' 116 7 = 'a' 97 8 = 'p' 112 9 = ' ' 32 10 = '2' 50 11 = '7' 55 12 = '5' 53 13 = ' ' 32 14 = '4' 52 15 = '1' 49 16 = '0' 48 17 = '\u0000' 0 18 = '\u0000' 0 19 = '\u0000' 0 20 = '\u0000' 0 21 = '\u0000' 0 22 = '\u0000' 0 23 = '\u0000' 0 24 = '\u0000' 0 25 = '\u0000' 0

I would like some guidance or a property to study, since my results are not being effective. As far as I can tell, this is not a permission issue, nor is it a thread issue, since Logs show the permission request (and the grant), an the thread lives for as long as the Receiver is being executed (but if I debug it for too long, then it gets killed after a while [30+ seconds])

The Receiver.onReceive:

public void onReceive(Context context, Intent intent) {
    String command = intent.getStringExtra("command");
    String touch = intent.getStringExtra("touch");
    if (Executor.isRootAvailable()) {
        Executor executor = new Executor();
        ArrayList<String> commands = new ArrayList<>();
        if (command != null) {
            if (command.length() > 0) {
                if (commands.add("input " + command)) {
                    executor.execute(commands);
                }
            }
        } else if (touch != null) {
            if (CursorSystem.isAlive) { // Always true for the executions
                CursorSystem.doInput();
                if (CursorSystem.state == CursorSystem.STATE) { // Always true for the executions
                    int[] coords = CursorSystem.coordinates;
                    if (coords != null) {
// This line is where (I guess) that the problem lies.
// CursorSystem.command is "tap", coords[0] and coords[1] is the result of (a view).getLocationOnScreen(coordinates)
// It results in a 2 positions int array with (x, y) coordinates, these values are always correct.
                        if (commands.add("input " + CursorSystem.command + " " + coords[0] + " " + coords[1])) {
                            executor.execute(commands);
                            CursorSystem.doInput();
                        } else {
                            // error...
                        }
                    } else {
                        // error...
                    }
                } else {
                    // error...
                }
            } else {
                error...
            }
        } else {
            error...
        }
    } else {
        error...
    }
}

The Executor.execute:

public final boolean execute(ArrayList<String> commands) {
    boolean resp = false;
    try {
        if (commands != null && commands.size() > 0) {
            Process suProcess = Runtime.getRuntime().exec("su");
            DataOutputStream dataOutputStream = new DataOutputStream(suProcess.getOutputStream());
            for (String currCommand : commands) {
                dataOutputStream.writeBytes(currCommand);
                dataOutputStream.writeBytes("\n");
                dataOutputStream.flush();
            }
            dataOutputStream.writeBytes("exit\n");
            dataOutputStream.flush();
            try {
                int suProcessRetval = suProcess.waitFor();
                return (suProcessRetval != 255); // Always yields 0
            } catch (Exception ex) {
                // errors...
            }
        } else {
            // error...
        }
    } catch (IOException ex) {
        Log.w(TAG, "IOException: ", ex);
    } catch (SecurityException ex) {
        Log.w(TAG, "SecurityException: ", ex);
    } catch (Exception ex) {
        Log.w(TAG, "Generic Exception: ", ex);
    }
    return resp;
}
Centro answered 22/12, 2015 at 19:26 Comment(12)
In your Executor.execute, you return resp which will always be false since you're not changing it anywhere. Is it how it's supposed to be?Longshoreman
Also could you please mention what exactly CursorSystem class is and format the value line if it can be? I'm guessing CursorSystem is your own class. It will be helpful to see its complete code.Longshoreman
@AkashAggarwal Yes, mainly because the code now is a mess of reviews. The code will eventually reach return (suProcessRetval != 255); that evaluates to true... Also, CursorSystem class has over two thousand lines of code. The main variables used are always set (never null at least), CursorSystem.isAlive is a boolean always debugged as true, most of other codes shown are actually different, and have been simplified for the question... At the same time, no LogCat errors appear, no Exceptions are thrown, no failures.... and no response.Centro
I think I'm getting on the same page as you are. I still don't understand the format of the value line. Could you please explain it more and also could you confirm that it is an output?Longshoreman
Well. CursorSystem is a Service that keeps on running. the isAlive (boolean) is a flag to indicate that the Service is executing correctly. doInput() is a Static function that executes the currently present task in CursorSystem, its triggered in several different areas of the application, but does not alter anything outside of CursorSystem. dataOutputStream is never (during debugging sessions) null.... Finally, on the executor.execute, its argument is an ArrayList of Strings. The Strings inside are always equivalent (meaning String (from run 0) equals String (from run 1))....Centro
It is worth noticing that the code has been executed on 3 different devices (Motorolla E3, Samsung J1 and Sony Experia), as well as on the emulator (in the emulator, it does not work, since its "core" is missing features the program uses). On all devices, the binary representation of each string is identical (when saved to a File). Finally its also worth noticing that the issue has been bypassed. Still, I want to know what could have happened that caused such a bug/issue. I personally believe it to be some "Guard" system that blocks commands, even in rooted devices. Or indirect windowsusedCentro
I don't understand this line I've been talking about: value = {char[26]@830031306976} 0 = 'i' 105 1 = 'n' 110 2 = 'p' 112 3 = 'u' 117 4 = 't' 116 5 = ' ' 32 6 = 't' 116 7 = 'a' 97 8 = 'p' 112 9 = ' ' 32 10 = '2' 50 11 = '7' 55 12 = '5' 53 13 = ' ' 32 14 = '4' 52 15 = '1' 49 16 = '0' 48 17 = '\u0000' 0 18 = '\u0000' 0 19 = '\u0000' 0 20 = '\u0000' 0 21 = '\u0000' 0 22 = '\u0000' 0 23 = '\u0000' 0 24 = '\u0000' 0 25 = '\u0000' 0Longshoreman
value is the debug position of the commands argument in the Executor.execute function. An ArrayList of Strings, with size 1, a String (Array of Char), with contents, 0....25 (default String allocation in that enviroment)...... While I understand that watching the code shown is likely to show some failure, I have some knowledge on what I am doing, and can pretty much tell that the issue is not inside my code, but rather in some indirect situation, or indirect activaton of Java Objects, that is blocking the processing somehow. While I studied this, some scenarios have been droppedCentro
You have mentioned there is no issue with the thread but I think Executor will run asynchronously, so your onReceive() function may be returning before your execute() function is finishing. So if your broadcast receiver is receiving intents when your app is not running then its possible that your application process is getting killed after returning from onReceive() function. Read about Broadcast Receiver Life Cycle here. Its no recomended to do asynchronous stuff in onReceiver(). Use services for this purpose.Treiber
Thanks for the info, however, I cant see "synchronicity" as an issue. Executor can both be created, or be executing, and the function will still be run (in debug, that is exactly what I am seeing). My application reaches all breakpoints and returns involved in the application. The onReceiver would merely "stack" more commands on the Executor. Also, the CursorSystem is a Service, that is running correctly. I can tell that due to this "problem" some code has been changed, and the issue is no longer reproducible. I can however state that this Receiver and the Services involved did not change.Centro
rather than appending strings, have you tried StringBuilder and converting to string ? StringBuilder sb=new StringBuilder("input ");sb.append(CursorSystem.command);sb.append(" ");sb.append(coords[0]);sb.append(coords[1]); pass String strToPass=sb.toString();Buttonwood
@Stallion Since Executor could send single commands, as well as batch commands, the argument is an ArrayList. I have directly sent String to that class, in order to test it. It works as expected.Centro
C
3

For anyone interested:

The problem found was that the CursorSystem used ImageViews, that aided the user to "target" what interaction was desired (and where). During execution, the command could be received, and the image be moved to where the command would happens, thus "receiving" the event.

Adding the following flags:

.LayoutParams.FLAG_NOT_FOCUSABLE
.LayoutParams.FLAG_NOT_TOUCHABLE
.LayoutParams.FLAG_LAYOUT_NO_LIMITS

Allowed the system to not consider the Views as interactable, as well as "draw" them outside of bounds, so that the user could "swipe" the device hidden menus, as well as click on a target that could potentially NOT exist. This caused some "Launcher" programs to intercept the MotionEvent, and do nothing.

The command was being executed successfully, just no result was being displayed, and caused us to interpret it as "doing nothing".

Centro answered 10/5, 2016 at 11:11 Comment(0)
C
-1

replace

 if (commands.add("input " + CursorSystem.command + " " + coords[0] + " " + coords[1])) {
                            executor.execute(commands);
                            CursorSystem.doInput();
                        }

with

String newCommand = "input " + CursorSystem.command + " " + coords[0] + " " + coords[1];
newCommand = newCommand.replace("\u0000", "").replace("\\u0000", "");// removes NUL chars and backslash+u0000
if(commands.add(newCommand)){
   executor.execute(commands);
   CursorSystem.doInput();
   }
Cad answered 9/5, 2016 at 10:7 Comment(4)
While this may answer the OP's question, posting solid code blocks without any explanation serves no educational purpose. Please improve your answer by explaining why this code is a solution or if it is based on OP's what has changed and for what reason.Ecumenism
I've commented my line of code : "// removes NUL chars and backslash+u0000 " isn't it enough?Cad
@LinhNguyen As stated in the question, the String argument is equal. There are no null characters or unwanted characters (such as a backlash, new line, line feed, etc.). The issue does not appear to be within the code, since the "fix" given to the application did not alter either Service or class within it.Centro
value = {char[26]@830031306976} 0 = 'i' 105 1 = 'n' 110 2 = 'p' 112 3 = 'u' 117 4 = 't' 116 5 = ' ' 32 6 = 't' 116 7 = 'a' 97 8 = 'p' 112 9 = ' ' 32 10 = '2' 50 11 = '7' 55 12 = '5' 53 13 = ' ' 32 14 = '4' 52 15 = '1' 49 16 = '0' 48 17 = '\u0000' 0 18 = '\u0000' 0 19 = '\u0000' 0 20 = '\u0000' 0 21 = '\u0000' 0 22 = '\u0000' 0 23 = '\u0000' 0 24 = '\u0000' 0 25 = '\u0000' 0 your log showing alot of null/blank character ?Cad

© 2022 - 2024 — McMap. All rights reserved.