-1

I'm trying to code, for an android app, a way to control my WD TV. In linux on my terminal I do this and it works perfectly: curl -s -d '{"remote":"n"}' -v http://192.168.0.3/cgi-bin/toServerValue.cgi

{"remote":"n"} is the json parameter sent for the OK button.

Here is my java code so far:

   try {
   String url="192.168.0.3/cgi-bin/toServerValue.cgi";
        URL object=new URL(url);

        HttpURLConnection con = (HttpURLConnection) object.openConnection();
        con.setDoOutput(true);
        con.setDoInput(true);
        con.setRequestProperty("Content-Type", application/json;charset=UTF-8");
        con.setRequestMethod("POST");

        JSONObject wd=new JSONObject();
        wd.put("remote", "n");

        OutputStreamWriter wr= new OutputStreamWriter(con.getOutputStream());
        wr.write(wd.toString());
        wr.flush();
        con.disconnect();


    } catch (MalformedURLException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (ProtocolException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } catch (JSONException e) {
        e.printStackTrace();
    }

thx for your help

Here is the logcat when I use http://

D/ViewRootImpl: ViewPostImeInputStage ACTION_DOWN
I/System.out: (HTTPLog)-Static: SBServiceAPI: getService class android.os.ServiceManager
I/System.out: (HTTPLog)-Static: isShipBuild true
I/System.out: (HTTPLog)-Thread-1-928574752: SmartBonding Enabling is true, SHIP_BUILD is true, log to file is false, DBG is false
D/AndroidRuntime: Shutting down VM
E/AndroidRuntime: FATAL EXCEPTION: main
          Process: net.tchouny.httppost, PID: 20138
          java.lang.IllegalStateException: Could not execute method for android:onClick
              at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:293)
              at android.view.View.performClick(View.java:5156)
              at android.view.View$PerformClick.run(View.java:20755)
              at android.os.Handler.handleCallback(Handler.java:739)
              at android.os.Handler.dispatchMessage(Handler.java:95)
              at android.os.Looper.loop(Looper.java:145)
              at android.app.ActivityThread.main(ActivityThread.java:5832)
              at java.lang.reflect.Method.invoke(Native Method)
              at java.lang.reflect.Method.invoke(Method.java:372)
              at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1399)
              at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1194)
           Caused by: java.lang.reflect.InvocationTargetException
              at java.lang.reflect.Method.invoke(Native Method)
              at java.lang.reflect.Method.invoke(Method.java:372)
              at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:288)
              at android.view.View.performClick(View.java:5156) 
              at android.view.View$PerformClick.run(View.java:20755) 
              at android.os.Handler.handleCallback(Handler.java:739) 
              at android.os.Handler.dispatchMessage(Handler.java:95) 
              at android.os.Looper.loop(Looper.java:145) 
              at android.app.ActivityThread.main(ActivityThread.java:5832) 
              at java.lang.reflect.Method.invoke(Native Method) 
              at java.lang.reflect.Method.invoke(Method.java:372) 
              at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1399) 
              at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1194) 
           Caused by: android.os.NetworkOnMainThreadException
              at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1147)
              at libcore.io.BlockGuardOs.connect(BlockGuardOs.java:110)
              at libcore.io.IoBridge.connectErrno(IoBridge.java:137)
              at libcore.io.IoBridge.connect(IoBridge.java:122)
              at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:183)
              at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:456)
              at java.net.Socket.connect(Socket.java:882)
              at com.android.okhttp.internal.Platform.connectSocket(Platform.java:139)
              at com.android.okhttp.Connection.connect(Connection.java:1171)
              at com.android.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:380)
              at com.android.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:289)
              at com.android.okhttp.internal.http.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:373)
              at com.android.okhttp.internal.http.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:106)
              at com.android.okhttp.internal.http.HttpURLConnectionImpl.getOutputStream(HttpURLConnectionImpl.java:208)
              at net.tchouny.httppost.MainActivity.httpPost(MainActivity.java:48)
              at java.lang.reflect.Method.invoke(Native Method) 
              at java.lang.reflect.Method.invoke(Method.java:372) 
              at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:288) 
              at android.view.View.performClick(View.java:5156) 
              at android.view.View$PerformClick.run(View.java:20755) 
              at android.os.Handler.handleCallback(Handler.java:739) 
              at android.os.Handler.dispatchMessage(Handler.java:95) 
              at android.os.Looper.loop(Looper.java:145) 
              at android.app.ActivityThread.main(ActivityThread.java:5832) 
              at java.lang.reflect.Method.invoke(Native Method) 
              at java.lang.reflect.Method.invoke(Method.java:372) 
              at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1399) 
              at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1194) 

It does not work. Here is what I've for the moment:

public class MainActivity extends AppCompatActivity {

private static final String wdURL = "http://192.168.0.3/cgi-bin/toServerValue.cgi";

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

}

       private void sendCommand(final String command) {
        new Thread(new Runnable() {
            public void run() {
                try {
                    URL object=new URL(wdURL);

                    HttpURLConnection con = (HttpURLConnection) object.openConnection();
                    con.setDoOutput(true);
                    con.setRequestProperty("Content-Type", "text/html;charset=iso-8859-1");
                    con.setRequestMethod("POST");


                    JSONObject wd =new JSONObject();
                    wd.put("remote", command);

                    OutputStreamWriter wr= new OutputStreamWriter(con.getOutputStream());
                    wr.write(wd.toString());
                    wr.flush();
                    con.disconnect();

                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

public void httpPost (View v){
    sendCommand("w");

}
}

and this is the logcat:

01-11 09:52:41.686 28555-28555/? E/Zygote: MountEmulatedStorage()
01-11 09:52:41.686 28555-28555/? E/Zygote: v2
01-11 09:52:41.686 28555-28555/? I/libpersona: KNOX_SDCARD checking    this for 10243
01-11 09:52:41.686 28555-28555/? I/libpersona: KNOX_SDCARD not a persona
01-11 09:52:41.716 28555-28555/? I/SELinux: Function: selinux_compare_spd_ram , priority [2] , priority version is VE=SEPF_SM-G900F_5.0-1_0032
01-11 09:52:41.716 28555-28555/? E/SELinux: [DEBUG] get_category: variable seinfo: default sensitivity: NULL, cateogry: NULL
01-11 09:52:41.716 28555-28555/? I/art: Late-enabling -Xcheck:jni
01-11 09:52:41.756 28555-28555/? D/TimaKeyStoreProvider: TimaSignature is unavailable
01-11 09:52:41.756 28555-28555/? D/ActivityThread: Added TimaKeyStore provider
01-11 09:52:41.806 28555-28555/net.tchouny.httppost D/ResourcesManager: creating new AssetManager and set to /data/app/net.tchouny.httppost-1/base.apk
01-11 09:52:41.836 28555-28555/net.tchouny.httppost I/InstantRun: Instant Run Runtime started. Android package is net.tchouny.httppost, real application class is null.
01-11 09:52:41.856 28555-28555/net.tchouny.httppost W/art: Failed to find OatDexFile for DexFile /data/data/net.tchouny.httppost/files/instant-run/dex/slice-slice_4-classes.dex ( canonical path /data/data/net.tchouny.httppost/files/instant-run/dex/slice-slice_4-classes.dex) with checksum 0x9d676064 in OatFile /data/data/net.tchouny.httppost/cache/slice-slice_4-classes.dex
01-11 09:52:42.366 28555-28555/net.tchouny.httppost W/art: Before Android 4.1, method android.graphics.PorterDuffColorFilter android.support.graphics.drawable.VectorDrawableCompat.updateTintFilter(android.graphics.PorterDuffColorFilter, android.content.res.ColorStateList, android.graphics.PorterDuff$Mode) would have incorrectly overridden the package-private method in android.graphics.drawable.Drawable
01-11 09:52:42.636 28555-28555/net.tchouny.httppost D/Activity: performCreate Call secproduct feature valuefalse
01-11 09:52:42.636 28555-28555/net.tchouny.httppost D/Activity: performCreate Call debug elastic valuetrue
01-11 09:52:42.666 28555-28643/net.tchouny.httppost D/OpenGLRenderer: Render dirty regions requested: true
01-11 09:52:42.736 28555-28643/net.tchouny.httppost I/Adreno-EGL: <qeglDrvAPI_eglInitialize:410>: EGL 1.4 QUALCOMM build:  ()
                                                              OpenGL ES Shader Compiler Version: E031.25.01.03
                                                              Build Date: 03/03/15 Tue
                                                              Local Branch: LA.BF.1.1_RB1_20150108_025_1077123_1158499
                                                              Remote Branch: 
                                                              Local Patches: 
                                                              Reconstruct Branch: 
01-11 09:52:42.736 28555-28643/net.tchouny.httppost I/OpenGLRenderer: Initialized EGL, version 1.4
01-11 09:52:42.756 28555-28643/net.tchouny.httppost I/OpenGLRenderer: HWUI protection enabled for context ,  &this =0xa1622088 ,&mEglDisplay = 1 , &mEglConfig = 8 
01-11 09:52:42.756 28555-28643/net.tchouny.httppost D/OpenGLRenderer: Enabling debug mode 0
01-11 09:52:42.886 28555-28555/net.tchouny.httppost I/Timeline: Timeline: Activity_idle id: android.os.BinderProxy@1969d169 time:253291220
01-11 09:52:46.256 28555-28555/net.tchouny.httppost D/ViewRootImpl: ViewPostImeInputStage ACTION_DOWN
01-11 09:52:46.436 28555-28771/net.tchouny.httppost I/System.out: (HTTPLog)-Static: SBServiceAPI: getService class android.os.ServiceManager
01-11 09:52:46.436 28555-28771/net.tchouny.httppost I/System.out: (HTTPLog)-Static: isShipBuild true
01-11 09:52:46.436 28555-28771/net.tchouny.httppost I/System.out: (HTTPLog)-Thread-87638-928574752: SmartBonding Enabling is true, SHIP_BUILD is true, log to file is false, DBG is false

Now my terminal with the following command:

tchouny@tchounyX220:~$ curl -s -d '{"remote":"w"}' -v http://192.168.0.3/cgi-bin/toServerValue.cgi   
*   Trying 192.168.0.3...
* Connected to 192.168.0.3 (192.168.0.3) port 80 (#0)
> POST /cgi-bin/toServerValue.cgi HTTP/1.1
> Host: 192.168.0.3
> User-Agent: curl/7.47.0
> Accept: */*
> Content-Length: 14
> Content-Type: application/x-www-form-urlencoded
> 
* upload completely sent off: 14 out of 14 bytes
< HTTP/1.1 200 OK
< Date: Wed, 11 Jan 2017 08:34:35 GMT
< Server: Apache
< Debug: haha
< X-Orion-Version: 1.0
< Transfer-Encoding: chunked
< Content-Type: text/html;charset=iso-8859-1;debug:[{"remote":"w"}]
< 
{ "success": 1 }
* Connection #0 to host 192.168.0.3 left intact
  • Probably not the cause, but you've missed out a quote mark on the line `con.setRequestProperty("Content-Type", application/json;charset=UTF-8");`. Just double-checking if that's also the case in your code. – Michael Dodd Jan 10 '17 at 15:25
  • Also, what exactly is going wrong? Is the app not compiling? Is the request not sending? Could you give us more details? – Michael Dodd Jan 10 '17 at 15:25
  • I have the quote mark, just a copy paste problem. – Laurent Duthoo Jan 10 '17 at 15:29
  • here is the logcat: – Laurent Duthoo Jan 10 '17 at 15:32
  • W/System.err: java.net.MalformedURLException: Protocol not found: 192.168.0.3/cgi-bin/toServerValue.cgi – Laurent Duthoo Jan 10 '17 at 15:34
  • Try putting a `http://` in front of your address. Alternatively, it might be worth using the [URI](http://docs.oracle.com/javase/6/docs/api/java/net/URI.html) class instead of URL, as per [jjnguy's answer here](http://stackoverflow.com/a/59851/469080) – Michael Dodd Jan 10 '17 at 15:37
  • Possible duplicate of [getting java exception: java.net.MalformedURLException: no protocol](http://stackoverflow.com/questions/59832/getting-java-exception-java-net-malformedurlexception-no-protocol) – Michael Dodd Jan 10 '17 at 15:42
  • Using http:// caused a fatal error and the app crashed – Laurent Duthoo Jan 10 '17 at 16:18
  • Can you post the logcat of that crash? Edit your original post, please don't post it as a comment. – Michael Dodd Jan 10 '17 at 16:20
  • Sorry I'm new on this site. I've clicked on edit and add the logcat when i use http:// – Laurent Duthoo Jan 10 '17 at 16:48
  • I've used URI builder and the app crashed as well – Laurent Duthoo Jan 10 '17 at 16:50
  • Yeah you're running network code on the main (UI) thread. That's blocked by Android. I'll post a detailed answer later, but for now move your code into an [AsyncTask](https://developer.android.com/reference/android/os/AsyncTask.html). This question will show you how - http://stackoverflow.com/questions/9413625/android-android-os-networkonmainthreadexception/9415736#9415736 – Michael Dodd Jan 10 '17 at 16:54
  • Ok I see won't be easy as I'm not an expert. I will try. Thx – Laurent Duthoo Jan 10 '17 at 17:14

2 Answers2

0

Currently you're trying to run a network operation on the main thread, which is blocked by Android. Therefore if you want to communicate with your WDTV, you'll need to do so on a background thread.

After knocking together a sample app to test on my own WDTV I can say that this method does work. The main difference is that I've set the URL to be read from a EditText, rather than being hard-coded.

/**
 * ButterKnife notation - don't copy verbatim,
 * copy what's inside the method and put into your button's onClickListener()
 */     
@OnClick(R.id.btn_send) public void send() {
    new Thread(new Runnable() {
        public void run() {
            sendCommand(txtCommand.getText().toString());
        }
    }).start();
}


/**
 * Don't run this method on the UI thread!
 */
private void sendCommand(String command) {
    InputStream in = null;
    OutputStream out = null;
    String reply = "";

    try {
        // Replace this with your hard-coded URL
        String url = String.format("http://%s/cgi-bin/toServerValue.cgi", txtIp.getText().toString());
        URL object=new URL(url);

        HttpURLConnection con = (HttpURLConnection) object.openConnection();
        con.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
        con.setRequestMethod("POST");
        con.setDoOutput(true);
        con.setDoInput(true);

        JSONObject json = new JSONObject();
        json.put("remote", command);

        out = con.getOutputStream();
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out));
        writer.write(json.toString());
        writer.flush();
        writer.close();
        out.close();

        // If we're interested in the reply...
        int respCode = con.getResponseCode();
        if (respCode == HttpURLConnection.HTTP_OK) {
            String line;
            in = con.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(in));
            while ((line = reader.readLine()) != null) {
                reply += line;
            }

            reader.close();
            in.close();

            // Display the returned JSON in a text box, just for confirmation.
            // Code not necessary for this example
            showResult(reply);
        } else {
            showResult("Failed to connect: " + respCode);
        }

        con.disconnect();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

Note how the method that sends the request is wrapped in a new Thread(...).start(); statement. This tells Android to run your code in a new background thread. However, note that this isn't a very efficient way of doing things. There are better ways of creating background threads that you'll learn in time, such as using AsyncTask and ExecutorService, but only dive into them when you feel ready.

And a screenshot to show the app: WDTV test app - successful command sent

Further reading - Processes and Threads

Michael Dodd
  • 10,102
  • 12
  • 51
  • 64
0

Here is the final answer. First thx Michael for your help.

I've made different tests and finally it does not make any difference if I use JSON or basic String. What is apparently important is the fact we get a response from the http Connection.

I've tested this:

btStart.setOnClickListener(new View.OnClickListener() {
        public void onClick(View v) {
            sendCommand("{\"remote\":\"w\"}");
        }
    });

}


private void sendCommand(final String command) {
        new Thread(new Runnable() {
            public void run() {

                InputStream in = null;
                OutputStream out = null;
                String reply = "";

                try {
                    String url = "http://192.168.0.3/cgi-bin/toServerValue.cgi";
                    URL object=new URL(url);

                    HttpURLConnection con = (HttpURLConnection) object.openConnection();
                    con.setRequestProperty("Content-Type", "text/html;charset=UTF-8");
                    con.setRequestMethod("POST");
                    con.setDoOutput(true);
                    //con.setDoInput(true);

                    //JSONObject json = new JSONObject();
                    //json.put("remote", command);
                    String httpPost = command;

                    out = con.getOutputStream();
                    BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out));
                    writer.write(command.toString());
                    writer.flush();
                    writer.close();
                    out.close();

                    // If we're interested in the reply...
                    int respCode = con.getResponseCode();
                    /*if (respCode == HttpURLConnection.HTTP_OK) {
                        String line;
                        in = con.getInputStream();
                        BufferedReader reader = new BufferedReader(new InputStreamReader(in));
                        while ((line = reader.readLine()) != null) {
                            reply += line;
                        }

                        reader.close();
                        in.close();

                        // Display the returned JSON in a text box, just for confirmation.
                        // Code not necessary for this example
                        tv.setText(reply);
                    } else {
                        tv.setText("Failed to connect: " + respCode);
                    }
*/
                    con.disconnect();
                } catch (Exception e) {
                    e.printStackTrace();
                }

            }
        }).start();
    }

and it works perfectly just because I have this: int respCode = con.getResponseCode(); If I remove the response it does not work. Now if I use instead of basic string a JSON format what is actually easier to query, the result is the same. With the getResponse it works and without it does not.

Interresting. Laurent