Showing posts with label Android Network Programming. Show all posts
Showing posts with label Android Network Programming. Show all posts

Wednesday, June 1, 2016

Android Datagram/UDP Server example


I posted "Java Datagram/UDP Server and Client, run on raspberry Pi" on my another blogspot. And last post show "Android Datagram/UDP Client example". This post show a Datagram/UDP Server run on Android.


MainActivity.java
package com.blogspot.android_er.androidudpserver;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.TextView;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Date;
import java.util.Enumeration;

public class MainActivity extends AppCompatActivity {

    private final static String TAG = MainActivity.class.getSimpleName();

    TextView infoIp, infoPort;
    TextView textViewState, textViewPrompt;

    static final int UdpServerPORT = 4445;
    UdpServerThread udpServerThread;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        infoIp = (TextView) findViewById(R.id.infoip);
        infoPort = (TextView) findViewById(R.id.infoport);
        textViewState = (TextView)findViewById(R.id.state);
        textViewPrompt = (TextView)findViewById(R.id.prompt);

        infoIp.setText(getIpAddress());
        infoPort.setText(String.valueOf(UdpServerPORT));
    }

    @Override
    protected void onStart() {
        udpServerThread = new UdpServerThread(UdpServerPORT);
        udpServerThread.start();
        super.onStart();
    }

    @Override
    protected void onStop() {
        if(udpServerThread != null){
            udpServerThread.setRunning(false);
            udpServerThread = null;
        }

        super.onStop();
    }

    private void updateState(final String state){
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                textViewState.setText(state);
            }
        });
    }

    private void updatePrompt(final String prompt){
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                textViewPrompt.append(prompt);
            }
        });
    }

    private class UdpServerThread extends Thread{

        int serverPort;
        DatagramSocket socket;

        boolean running;

        public UdpServerThread(int serverPort) {
            super();
            this.serverPort = serverPort;
        }

        public void setRunning(boolean running){
            this.running = running;
        }

        @Override
        public void run() {

            running = true;

            try {
                updateState("Starting UDP Server");
                socket = new DatagramSocket(serverPort);

                updateState("UDP Server is running");
                Log.e(TAG, "UDP Server is running");

                while(running){
                    byte[] buf = new byte[256];

                    // receive request
                    DatagramPacket packet = new DatagramPacket(buf, buf.length);
                    socket.receive(packet);     //this code block the program flow

                    // send the response to the client at "address" and "port"
                    InetAddress address = packet.getAddress();
                    int port = packet.getPort();

                    updatePrompt("Request from: " + address + ":" + port + "\n");

                    String dString = new Date().toString() + "\n"
                            + "Your address " + address.toString() + ":" + String.valueOf(port);
                    buf = dString.getBytes();
                    packet = new DatagramPacket(buf, buf.length, address, port);
                    socket.send(packet);

                }

                Log.e(TAG, "UDP Server ended");

            } catch (SocketException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if(socket != null){
                    socket.close();
                    Log.e(TAG, "socket.close()");
                }
            }
        }
    }

    private String getIpAddress() {
        String ip = "";
        try {
            Enumeration<NetworkInterface> enumNetworkInterfaces = NetworkInterface
                    .getNetworkInterfaces();
            while (enumNetworkInterfaces.hasMoreElements()) {
                NetworkInterface networkInterface = enumNetworkInterfaces
                        .nextElement();
                Enumeration<InetAddress> enumInetAddress = networkInterface
                        .getInetAddresses();
                while (enumInetAddress.hasMoreElements()) {
                    InetAddress inetAddress = enumInetAddress.nextElement();

                    if (inetAddress.isSiteLocalAddress()) {
                        ip += "SiteLocalAddress: "
                                + inetAddress.getHostAddress() + "\n";
                    }

                }

            }

        } catch (SocketException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            ip += "Something Wrong! " + e.toString() + "\n";
        }

        return ip;
    }
}


activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    android:orientation="vertical"
    tools:context="com.blogspot.android_er.androidudpserver.MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:autoLink="web"
        android:text="http://android-er.blogspot.com/"
        android:textStyle="bold" />

    <TextView
        android:id="@+id/infoip"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textStyle="italic" />

    <TextView
        android:id="@+id/infoport"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textStyle="italic" />

    <TextView
        android:id="@+id/state"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="un-initiated"
        android:textSize="20dp"/>

    <TextView
        android:id="@+id/prompt"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="18dp"/>
</LinearLayout>


uses-permission of "android.permission.INTERNET" is needed in AndroidManifest.xml

Remark about life-cycle:
In this example, the DatagramSocket server is run in background thread. I haven't handle the life-cycle very well (Actually I don't think any application will have UI like this example). Consider the cases:

Case One:
- Start the app, the activity display on screen and the DatagramSocket opened in associated thread.
- the code socket.receive(packet) block the program flow, so the thread stay here and waiting data request.
- Exit the app. It will set running to false, to request the thread to stop. But the thread is blocked in socket.receive(packet), so it's still running.
- Restart the app, the new thread cannot open the DatagramSocket, because it's still held by old thread.
- Client send a request, the DatagramSocket server response the request and exit socket.receive(packet), and check running and exit.
- In this case, the current activity and associated thread have no DatagramSocket opened!

Case Two:
- Start the app, the activity display on screen and the DatagramSocket opened in associated thread.
- the code socket.receive(packet) block the program flow, so the thread stay here and waiting data request.
- Exit the app. It will set running to false, to request the thread to stop. But the thread is blocked in socket.receive(packet), so it's still running.
- Client send a request, the DatagramSocket server response the request and exit socket.receive(packet), and check running and exit.
- Restart the app, and open the DatagramSocket.
- In this case, the current activity and associated thread can open DatagramSocket and work as expected.


download filesDownload the files .

Sunday, May 29, 2016

Android client example 2, communicate with Java Server run on Raspberry Pi


Refer to the previous Android client example to send message to Java server on Raspberry Pi. The client Android Client connect To Java Server, and send something then close socket. This example show how Android Client connect to Java Server (run on raspberry Pi) and keep connected, and send to and receive from Server.


For the Server side, it's Java Echo Server to send back the received data to the sender.

Please notice:
Suppose the Disconnect button is used to disconnect from server once clicked. In this example, it cannot actually. Because the code bufferedReader.readLine() in ClientThread.java will block the program. The Disconnect request will wait until anything received to return from bufferedReader.readLine().

Create ClientThread.java to handle network related job in background thread.
package com.blogspot.android_er.androidclient;

import android.os.Message;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;

public class ClientThread extends Thread{

    String dstAddress;
    int dstPort;
    private boolean running;
    MainActivity.ClientHandler handler;

    Socket socket;
    PrintWriter printWriter;
    BufferedReader bufferedReader;

    public ClientThread(String addr, int port, MainActivity.ClientHandler handler) {
        super();
        dstAddress = addr;
        dstPort = port;
        this.handler = handler;
    }

    public void setRunning(boolean running){
        this.running = running;
    }

    private void sendState(String state){
        handler.sendMessage(
                Message.obtain(handler,
                        MainActivity.ClientHandler.UPDATE_STATE, state));
    }

    public void txMsg(String msgToSend){
        if(printWriter != null){
            printWriter.println(msgToSend);
        }
    }

    @Override
    public void run() {
        sendState("connecting...");

        running = true;

        try {
            socket = new Socket(dstAddress, dstPort);
            sendState("connected");

            OutputStream outputStream = socket.getOutputStream();
            printWriter = new PrintWriter(outputStream, true);

            InputStream inputStream = socket.getInputStream();
            InputStreamReader inputStreamReader =
                    new InputStreamReader(inputStream);
            bufferedReader = new BufferedReader(inputStreamReader);

            while(running){

                //bufferedReader block the code
                String line = bufferedReader.readLine();
                if(line != null){
                    handler.sendMessage(
                            Message.obtain(handler,
                                    MainActivity.ClientHandler.UPDATE_MSG, line));
                }

            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(bufferedReader != null){
                try {
                    bufferedReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            if(printWriter != null){
                printWriter.close();
            }

            if(socket != null){
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        handler.sendEmptyMessage(MainActivity.ClientHandler.UPDATE_END);
    }
}


MainActivity.java
package com.blogspot.android_er.androidclient;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    EditText editTextAddress, editTextPort, editTextMsg;
    Button buttonConnect, buttonDisconnect, buttonSend;
    TextView textViewState, textViewRx;

    ClientHandler clientHandler;
    ClientThread clientThread;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        editTextAddress = (EditText) findViewById(R.id.address);
        editTextPort = (EditText) findViewById(R.id.port);
        editTextMsg = (EditText) findViewById(R.id.msgtosend);
        buttonConnect = (Button) findViewById(R.id.connect);
        buttonDisconnect = (Button) findViewById(R.id.disconnect);
        buttonSend = (Button)findViewById(R.id.send);
        textViewState = (TextView)findViewById(R.id.state);
        textViewRx = (TextView)findViewById(R.id.received);

        buttonDisconnect.setEnabled(false);
        buttonSend.setEnabled(false);

        buttonConnect.setOnClickListener(buttonConnectOnClickListener);
        buttonDisconnect.setOnClickListener(buttonDisConnectOnClickListener);
        buttonSend.setOnClickListener(buttonSendOnClickListener);

        clientHandler = new ClientHandler(this);
    }

    View.OnClickListener buttonConnectOnClickListener =
            new View.OnClickListener() {

                @Override
                public void onClick(View arg0) {

                    clientThread = new ClientThread(
                            editTextAddress.getText().toString(),
                            Integer.parseInt(editTextPort.getText().toString()),
                            clientHandler);
                    clientThread.start();

                    buttonConnect.setEnabled(false);
                    buttonDisconnect.setEnabled(true);
                    buttonSend.setEnabled(true);
                }
            };

    View.OnClickListener buttonDisConnectOnClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if(clientThread != null){
                clientThread.setRunning(false);
            }

        }
    };

    View.OnClickListener buttonSendOnClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if(clientThread != null){
                String msgToSend = editTextMsg.getText().toString();
                clientThread.txMsg(msgToSend);
            }
        }
    };

    private void updateState(String state){
        textViewState.setText(state);
    }

    private void updateRxMsg(String rxmsg){
        textViewRx.append(rxmsg + "\n");
    }

    private void clientEnd(){
        clientThread = null;
        textViewState.setText("clientEnd()");
        buttonConnect.setEnabled(true);
        buttonDisconnect.setEnabled(false);
        buttonSend.setEnabled(false);

    }

    public static class ClientHandler extends Handler {
        public static final int UPDATE_STATE = 0;
        public static final int UPDATE_MSG = 1;
        public static final int UPDATE_END = 2;
        private MainActivity parent;

        public ClientHandler(MainActivity parent) {
            super();
            this.parent = parent;
        }

        @Override
        public void handleMessage(Message msg) {

            switch (msg.what){
                case UPDATE_STATE:
                    parent.updateState((String)msg.obj);
                    break;
                case UPDATE_MSG:
                    parent.updateRxMsg((String)msg.obj);
                    break;
                case UPDATE_END:
                    parent.clientEnd();
                    break;
                default:
                    super.handleMessage(msg);
            }

        }

    }
}


activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    android:orientation="vertical"
    tools:context="com.blogspot.android_er.androidclient.MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:autoLink="web"
        android:text="http://android-er.blogspot.com/"
        android:textStyle="bold" />
    <EditText
        android:id="@+id/address"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="192.168."
        android:hint="dstAddress" />
    <EditText
        android:id="@+id/port"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="8000"
        android:hint="dstPort" />
    <Button
        android:id="@+id/connect"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Connect"/>
    <Button
        android:id="@+id/disconnect"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Disconnect"/>
    <TextView
        android:id="@+id/state"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="un-initiated"
        android:textSize="20dp"/>
    <EditText
        android:id="@+id/msgtosend"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="msg to send..." />
    <Button
        android:id="@+id/send"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Send"/>
    <TextView
        android:id="@+id/received"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="18dp"/>
</LinearLayout>


uses-permission of "android.permission.INTERNET" is needed in AndroidManifest.xml


download filesDownload the files .

Monday, May 23, 2016

Android client to send message to Java server on Raspberry Pi


It's a Java client and server exercise in my another blogspot HelloraspberryPi. The client connect to server and send something, the server simple print the received message and close.

It's Android version of the client, to connect to server and send something.


MainActivity.java
package com.blogspot.android_er.androidclient;

import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.net.UnknownHostException;

public class MainActivity extends AppCompatActivity {

    EditText editTextAddress, editTextPort, editTextMsg;
    Button buttonConnect;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        editTextAddress = (EditText) findViewById(R.id.address);
        editTextPort = (EditText) findViewById(R.id.port);
        editTextMsg = (EditText) findViewById(R.id.msgtosend);
        buttonConnect = (Button) findViewById(R.id.connect);

        buttonConnect.setOnClickListener(buttonConnectOnClickListener);
    }

    View.OnClickListener buttonConnectOnClickListener =
            new View.OnClickListener() {

                @Override
                public void onClick(View arg0) {
                    MyClientTask myClientTask = new MyClientTask(
                            editTextAddress.getText().toString(),
                            Integer.parseInt(editTextPort.getText().toString()));
                    myClientTask.execute(editTextMsg.getText().toString());
                }
            };

    public class MyClientTask extends AsyncTask<String, Void, Void> {

        String dstAddress;
        int dstPort;
        String response;

        MyClientTask(String addr, int port) {
            dstAddress = addr;
            dstPort = port;
        }

        @Override
        protected Void doInBackground(String... params) {
            try {
                Socket socket = new Socket(dstAddress, dstPort);

                OutputStream outputStream = socket.getOutputStream();
                PrintStream printStream = new PrintStream(outputStream);
                printStream.print(params[0]);

                socket.close();

            } catch (UnknownHostException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return null;
        }

    }
}


layout/activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    android:orientation="vertical"
    tools:context="com.blogspot.android_er.androidclient.MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:autoLink="web"
        android:text="http://android-er.blogspot.com/"
        android:textStyle="bold" />
    <EditText
        android:id="@+id/address"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="dstAddress" />
    <EditText
        android:id="@+id/port"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="dstPort" />
    <EditText
        android:id="@+id/msgtosend"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="msg to send..." />
    <Button
        android:id="@+id/connect"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Connect and send..."/>

</LinearLayout>


uses-permission of "android.permission.INTERNET" is needed in AndroidManifest.xml

The Java code of the server side, refer to "Java Network exercise: client and server - client send something to server".

The next example "Android client example 2, communicate with Java Server run on Raspberry Pi" show how Android Client connect to Java Server (run on raspberry Pi) and keep connected, and send to and receive from Server.

Wednesday, March 30, 2016

Load something from Internet using URLConnection and ReadableByteChannel, in Thread

Last example show "Load something from Internet using URLConnection and BufferedReader, in Thread". Here is another example using java.nio.channels.ReadableByteChannel.

Read operations are synchronous on a ReadableByteChannel, that is, if a read is already in progress on the channel then subsequent reads will block until the first read completes. It is undefined whether non-read operations will block.


MainActivity.java
package com.blogspot.android_er.androidinternet;

import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;
import android.widget.Toast;

import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;

public class MainActivity extends AppCompatActivity {

    TextView textResult;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textResult = (TextView)findViewById(R.id.tresult);

        MyThread myThread = new MyThread("http://android-er.blogspot.com", textResult);
        myThread.start();
    }

    class MyThread extends Thread{
        String target;
        TextView textviewResult;

        private Handler handler = new Handler();

        public MyThread(String target, TextView textviewResult) {
            super();
            this.target = target;
            this.textviewResult = textviewResult;
        }

        @Override
        public void run() {
            String result = "";

            URL url = null;
            try {
                url = new URL(target);
                URLConnection urlConnection = url.openConnection();
                InputStream inputStream = urlConnection.getInputStream();

                ReadableByteChannel readableByteChannel = Channels.newChannel(inputStream);
                ByteBuffer buffer = ByteBuffer.allocate(1024);

                while (readableByteChannel.read(buffer) > 0)
                {
                    result += new String(buffer.array());
                    buffer.clear();
                }

            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }

            final String finalResult = result;
            handler.post(new Runnable() {
                @Override
                public void run() {
                    textviewResult.setText(finalResult);
                    Toast.makeText(MainActivity.this,
                            "finished", Toast.LENGTH_LONG).show();
                }
            });
        }
    }
}


For layout and AndroidManifest.xml, refer to the previous post "Load something from Internet using URLConnection and BufferedReader, in AsyncTask".


Tuesday, March 29, 2016

Load something from Internet using URLConnection and BufferedReader, in Thread

Previous post show how to Load something from Internet using URLConnection and BufferedReader, in AsyncTask. This post show how to do it in Thread, and update UI using Handler.



MainActivity.java
package com.blogspot.android_er.androidinternet;

import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;
import android.widget.Toast;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;

public class MainActivity extends AppCompatActivity {

    TextView textResult;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textResult = (TextView)findViewById(R.id.tresult);

        MyThread myThread = new MyThread("http://android-er.blogspot.com", textResult);
        myThread.start();
    }

    class MyThread extends Thread{
        String target;
        TextView textviewResult;

        private Handler handler = new Handler();

        public MyThread(String target, TextView textviewResult) {
            super();
            this.target = target;
            this.textviewResult = textviewResult;
        }

        @Override
        public void run() {
            String result = "";

            try {
                URL url = new URL(target);
                URLConnection urlConnection = url.openConnection();
                InputStream inputStream = urlConnection.getInputStream();
                InputStreamReader inputStreamReader =
                        new InputStreamReader(inputStream);
                BufferedReader buffReader = new BufferedReader(inputStreamReader);

                String line;
                while ((line = buffReader.readLine()) != null) {
                    result += line;
                }

            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }

            final String finalResult = result;
            handler.post(new Runnable() {
                @Override
                public void run() {
                    textviewResult.setText(finalResult);
                    Toast.makeText(MainActivity.this,
                            "finished", Toast.LENGTH_LONG).show();
                }
            });
        }
    }
}


For layout and AndroidManifest.xml, refer to the previous post "Load something from Internet using URLConnection and BufferedReader, in AsyncTask".

Related:
Load something from Internet using URLConnection and ReadableByteChannel, in Thread

Load something from Internet using URLConnection and BufferedReader, in AsyncTask

Here is example to load something from Internet using URLConnection and BufferedReader, in AsyncTask.

MainActivity.java
package com.blogspot.android_er.androidinternet;

import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import android.widget.Toast;

import org.w3c.dom.Text;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;

public class MainActivity extends AppCompatActivity {

    TextView textResult;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textResult = (TextView)findViewById(R.id.tresult);

        MyAsyncTask myAsyncTask = new MyAsyncTask(textResult);
        myAsyncTask.execute("http://android-er.blogspot.com");
    }

    class MyAsyncTask extends AsyncTask<String, Void, String> {

        TextView textviewResult;

        public MyAsyncTask(TextView textviewResult) {
            super();
            this.textviewResult = textviewResult;
        }

        @Override
        protected String doInBackground(String... params) {
            String result = "";

            try {
                URL url = new URL(params[0]);
                URLConnection urlConnection = url.openConnection();
                InputStream inputStream = urlConnection.getInputStream();
                InputStreamReader inputStreamReader =
                        new InputStreamReader(inputStream);
                BufferedReader buffReader = new BufferedReader(inputStreamReader);

                String line;
                while ((line = buffReader.readLine()) != null) {
                    result += line;
                }

            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }

            return result;
        }

        @Override
        protected void onPostExecute(String s) {
            textviewResult.setText(s);

            Toast.makeText(MainActivity.this,
                    "DONE", Toast.LENGTH_LONG).show();
            super.onPostExecute(s);
        }
    }
}


activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp"
    tools:context="com.blogspot.android_er.androidinternet.MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:autoLink="web"
        android:text="http://android-er.blogspot.com/"
        android:textStyle="bold" />

    <ScrollView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">
    <TextView
        android:id="@+id/tresult"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    </ScrollView>
</LinearLayout>


uses-permission of "android.permission.INTERNET" is needed in src/main/AndroidManifest.xml.


Add uses-permission of "android.permission.INTERNET" to AndroidManifest.xml

To access Internet in your app, you have to add uses-permission of "android.permission.INTERNET" to AndroidManifest.xml. Shown in this video:


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.blogspot.android_er.androidinternet">

    <uses-permission android:name="android.permission.INTERNET"/>
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

Related:
Load something from Internet using URLConnection and BufferedReader, in Thread

Sunday, December 27, 2015

Android Chat example, with server sending individual message to specify client.


Refer to my old post of Simple Android Chat Application, server side, and client side. Function to send sending individual message to specify client is added in server side.


uses-permission of "android.permission.INTERNET" is needed in AndroidManifest.xml, for both server and client.


Server side:

MainActivity.java
package com.blogspot.android_er.androidchatserver;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    static final int SocketServerPORT = 8080;

    TextView infoIp, infoPort, chatMsg;
    Spinner spUsers;
    ArrayAdapter<ChatClient> spUsersAdapter;
    Button btnSentTo;

    String msgLog = "";

    List<ChatClient> userList;

    ServerSocket serverSocket;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        infoIp = (TextView) findViewById(R.id.infoip);
        infoPort = (TextView) findViewById(R.id.infoport);
        chatMsg = (TextView) findViewById(R.id.chatmsg);

        spUsers = (Spinner) findViewById(R.id.spusers);
        userList = new ArrayList<ChatClient>();
        spUsersAdapter = new ArrayAdapter<ChatClient>(
                MainActivity.this, android.R.layout.simple_spinner_item, userList);
        spUsersAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        spUsers.setAdapter(spUsersAdapter);

        btnSentTo = (Button)findViewById(R.id.sentto);
        btnSentTo.setOnClickListener(btnSentToOnClickListener);

        infoIp.setText(getIpAddress());

        ChatServerThread chatServerThread = new ChatServerThread();
        chatServerThread.start();
    }

    View.OnClickListener btnSentToOnClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            ChatClient client = (ChatClient)spUsers.getSelectedItem();
            if(client != null){
                String dummyMsg = "Dummy message from server.\n";
                client.chatThread.sendMsg(dummyMsg);
                msgLog += "- Dummy message to " + client.name + "\n";
                chatMsg.setText(msgLog);

            }else{
                Toast.makeText(MainActivity.this, "No user connected", Toast.LENGTH_LONG).show();
            }
        }
    };

    @Override
    protected void onDestroy() {
        super.onDestroy();

        if (serverSocket != null) {
            try {
                serverSocket.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

    private class ChatServerThread extends Thread {

        @Override
        public void run() {
            Socket socket = null;

            try {
                serverSocket = new ServerSocket(SocketServerPORT);
                MainActivity.this.runOnUiThread(new Runnable() {

                    @Override
                    public void run() {
                        infoPort.setText("I'm waiting here: "
                                + serverSocket.getLocalPort());
                    }
                });

                while (true) {
                    socket = serverSocket.accept();
                    ChatClient client = new ChatClient();
                    userList.add(client);
                    ConnectThread connectThread = new ConnectThread(client, socket);
                    connectThread.start();

                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            spUsersAdapter.notifyDataSetChanged();
                        }
                    });
                }

            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (socket != null) {
                    try {
                        socket.close();
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }

        }

    }

    private class ConnectThread extends Thread {

        Socket socket;
        ChatClient connectClient;
        String msgToSend = "";

        ConnectThread(ChatClient client, Socket socket){
            connectClient = client;
            this.socket= socket;
            client.socket = socket;
            client.chatThread = this;
        }

        @Override
        public void run() {
            DataInputStream dataInputStream = null;
            DataOutputStream dataOutputStream = null;

            try {
                dataInputStream = new DataInputStream(socket.getInputStream());
                dataOutputStream = new DataOutputStream(socket.getOutputStream());

                String n = dataInputStream.readUTF();

                connectClient.name = n;

                msgLog += connectClient.name + " connected@" +
                        connectClient.socket.getInetAddress() +
                        ":" + connectClient.socket.getPort() + "\n";
                MainActivity.this.runOnUiThread(new Runnable() {

                    @Override
                    public void run() {
                        chatMsg.setText(msgLog);
                    }
                });

                dataOutputStream.writeUTF("Welcome " + n + "\n");
                dataOutputStream.flush();

                broadcastMsg(n + " join our chat.\n");

                while (true) {
                    if (dataInputStream.available() > 0) {
                        String newMsg = dataInputStream.readUTF();


                        msgLog += n + ": " + newMsg;
                        MainActivity.this.runOnUiThread(new Runnable() {

                            @Override
                            public void run() {
                                chatMsg.setText(msgLog);
                            }
                        });

                        broadcastMsg(n + ": " + newMsg);
                    }

                    if(!msgToSend.equals("")){
                        dataOutputStream.writeUTF(msgToSend);
                        dataOutputStream.flush();
                        msgToSend = "";
                    }

                }

            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (dataInputStream != null) {
                    try {
                        dataInputStream.close();
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }

                if (dataOutputStream != null) {
                    try {
                        dataOutputStream.close();
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }

                userList.remove(connectClient);

                MainActivity.this.runOnUiThread(new Runnable() {

                    @Override
                    public void run() {
                        spUsersAdapter.notifyDataSetChanged();
                        Toast.makeText(MainActivity.this,
                                connectClient.name + " removed.", Toast.LENGTH_LONG).show();

                        msgLog += "-- " + connectClient.name + " leaved\n";
                        MainActivity.this.runOnUiThread(new Runnable() {

                            @Override
                            public void run() {
                                chatMsg.setText(msgLog);
                            }
                        });

                        broadcastMsg("-- " + connectClient.name + " leaved\n");
                    }
                });
            }

        }

        private void sendMsg(String msg){
            msgToSend = msg;
        }

    }

    private void broadcastMsg(String msg){
        for(int i=0; i<userList.size(); i++){
            userList.get(i).chatThread.sendMsg(msg);
            msgLog += "- send to " + userList.get(i).name + "\n";
        }

        MainActivity.this.runOnUiThread(new Runnable() {

            @Override
            public void run() {
                chatMsg.setText(msgLog);
            }
        });
    }

    private String getIpAddress() {
        String ip = "";
        try {
            Enumeration<NetworkInterface> enumNetworkInterfaces = NetworkInterface
                    .getNetworkInterfaces();
            while (enumNetworkInterfaces.hasMoreElements()) {
                NetworkInterface networkInterface = enumNetworkInterfaces
                        .nextElement();
                Enumeration<InetAddress> enumInetAddress = networkInterface
                        .getInetAddresses();
                while (enumInetAddress.hasMoreElements()) {
                    InetAddress inetAddress = enumInetAddress.nextElement();

                    if (inetAddress.isSiteLocalAddress()) {
                        ip += "SiteLocalAddress: "
                                + inetAddress.getHostAddress() + "\n";
                    }

                }

            }

        } catch (SocketException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            ip += "Something Wrong! " + e.toString() + "\n";
        }

        return ip;
    }

    class ChatClient {
        String name;
        Socket socket;
        ConnectThread chatThread;

        @Override
        public String toString() {
            return name + ": " + socket.getInetAddress().getHostAddress();
        }
    }
}


activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    android:orientation="vertical"
    tools:context="com.blogspot.android_er.androidchatserver.MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:autoLink="web"
        android:text="http://android-er.blogspot.com/"
        android:textStyle="bold" />

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Char Server"
        android:textStyle="bold" />

    <TextView
        android:id="@+id/infoport"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textStyle="italic" />

    <TextView
        android:id="@+id/infoip"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textStyle="italic" />

    <Spinner
        android:id="@+id/spusers"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

    <Button
        android:id="@+id/sentto"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Sent msg to individual user"/>

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent" >

        <TextView
            android:id="@+id/chatmsg"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    </ScrollView>
</LinearLayout>



download filesDownload the server side files (Android Studio Format) .

Client side same as in the post "Simple Android Chat Application, client side" in Android Studio form.

download filesDownload the client side files (Android Studio Format) .

Monday, December 21, 2015

Get HostName of WiFi hotspot clients, and check if it is still connected.


Previous posts show how to "Retrieve IP and MAC addresses from /proc/net/arp" and "Lookup manufacturer/vendor info from MAC address". This post show how to get the Host Name of the connected clients.

It can be noted that if we call getCanonicalHostName() or getHostName() methods of the InetAddressobjects, it will return  the IP address. If we call getLocalHost().getCanonicalHostName() or getLocalHost().getHostName() methods, it will return the host name, "localhost".

Also, if a devices disconnected, the file /proc/net/arp will not updated immediately. We can determine if it is still connected by calling inetAddress.isReachable(timeout). If false returned, means it is disconnected.

MainActivity.java
package com.blogspot.android_er.androidlistclient;

import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

    Button btnRead;
    TextView textResult;

    ListView listViewNode;
    ArrayList<Node> listNote;
    ArrayAdapter<Node> adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btnRead = (Button)findViewById(R.id.readclient);
        textResult = (TextView)findViewById(R.id.result);

        listViewNode = (ListView)findViewById(R.id.nodelist);
        listNote = new ArrayList<>();
        ArrayAdapter<Node> adapter =
                new ArrayAdapter<Node>(
                        MainActivity.this,
                        android.R.layout.simple_list_item_1,
                        listNote);
        listViewNode.setAdapter(adapter);

        btnRead.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new TaskReadAddresses(listNote, listViewNode).execute();
            }
        });
    }

    class Node {
        String ip;
        String mac;
        String CanonicalHostName;
        String HostName;
        String LocalHostCanonicalHostName;
        String LocalHostHostName;
        String remark;
        boolean isReachable;

        Node(String ip, String mac){
            this.ip = ip;
            this.mac = mac;
            queryHost();
        }

        @Override
        public String toString() {
            return "IP: " + ip + "\n" +
                    "MAC: " + mac + "\n" +
                    "CanonicalHostName:\t" + CanonicalHostName + "\n" +
                    "HostName:\t" + HostName + "\n" +
                    "getLocalHost().getCanonicalHostName():\t" + LocalHostCanonicalHostName + "\n" +
                    "getLocalHost().getHostName():\t" + LocalHostHostName + "\n" +
                    "isReachable: " + isReachable +
                    "\n" + remark;
        }

        private void queryHost(){
            try {
                InetAddress inetAddress = InetAddress.getByName(ip);
                CanonicalHostName = inetAddress.getCanonicalHostName();
                HostName = inetAddress.getHostName();
                LocalHostCanonicalHostName = inetAddress.getLocalHost().getCanonicalHostName();
                LocalHostHostName = inetAddress.getLocalHost().getHostName();
                isReachable = inetAddress.isReachable(3000);

            } catch (UnknownHostException e) {
                e.printStackTrace();
                remark = e.getMessage();
            } catch (IOException e) {
                e.printStackTrace();
                remark = e.getMessage();
            }
        }
    }

    private class TaskReadAddresses extends AsyncTask<Void, Node, Void> {

        ArrayList<Node> array;
        ListView listView;

        TaskReadAddresses(ArrayList<Node> array, ListView v){
            listView = v;
            this.array = array;
            array.clear();
            textResult.setText("querying...");
        }

        @Override
        protected Void doInBackground(Void... params) {
            readAddresses();
            return null;
        }

        @Override
        protected void onPostExecute(Void aVoid) {
            textResult.setText("Done");
        }

        @Override
        protected void onProgressUpdate(Node... values) {
            listNote.add(values[0]);
            ((ArrayAdapter)(listView.getAdapter())).notifyDataSetChanged();
        }

        private void readAddresses() {
            BufferedReader bufferedReader = null;

            try {
                bufferedReader = new BufferedReader(new FileReader("/proc/net/arp"));

                String line;
                while ((line = bufferedReader.readLine()) != null) {
                    String[] splitted = line.split(" +");
                    if (splitted != null && splitted.length >= 4) {
                        String ip = splitted[0];
                        String mac = splitted[3];
                        if (mac.matches("..:..:..:..:..:..")) {
                            Node thisNode = new Node(ip, mac);
                            publishProgress(thisNode);
                        }
                    }
                }

            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally{
                try {
                    bufferedReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}



Sunday, December 20, 2015

Lookup manufacturer info by MAC address, using www.macvendorlookup.com API

Last post show how to "Retrieve IP and MAC addresses of Android WiFi tethering clients from /proc/net/arp". This example show how to get vendor/manufacturer info of the associated MAC addresses.

(You can download runnable APK from link on bottom)


http://www.macvendorlookup.com/api provide API to lookup MAC Address Vendor/Manufacturer info.

Notice that this example haven't handle error condition.


MainActivity.java
package com.blogspot.android_er.androidlistclient;

import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

    Button btnRead;
    TextView textResult;

    ListView listViewNode;
    ArrayList<Node> listNote;
    ArrayAdapter<Node> adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btnRead = (Button)findViewById(R.id.readclient);
        textResult = (TextView)findViewById(R.id.result);

        listViewNode = (ListView)findViewById(R.id.nodelist);
        listNote = new ArrayList<>();
        ArrayAdapter<Node> adapter =
                new ArrayAdapter<Node>(
                        MainActivity.this,
                        android.R.layout.simple_list_item_1,
                        listNote);
        listViewNode.setAdapter(adapter);

        listViewNode.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                Node node = (Node) parent.getAdapter().getItem(position);
                Toast.makeText(MainActivity.this,
                        "MAC:\t" + node.mac + "\n" +
                                "IP:\t" + node.ip + "\n" +
                                "company:\t" + node.company + "\n" +
                                "country:\t" + node.country + "\n" +
                                "addressL1:\t" + node.addressL1 + "\n" +
                                "addressL2:\t" + node.addressL2 + "\n" +
                                "addressL3:\t" + node.addressL3 + "\n" +
                                "type:\t" + node.type + "\n" +
                                "startHex:\t" + node.startHex + "\n" +
                                "endHex:\t" + node.endHex + "\n" +
                                "startDec:\t" + node.startDec + "\n" +
                                "endDec:\t" + node.endDec,
                        Toast.LENGTH_SHORT).show();
            }
        });

        btnRead.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new TaskReadAddresses(listNote, listViewNode).execute();
            }
        });
    }



    class Node {
        String ip;
        String mac;

        String jsonBody;
        String startHex;
        String endHex;
        String startDec;
        String endDec;
        String company;
        String addressL1;
        String addressL2;
        String addressL3;
        String country;
        String type;

        String remark;

        String queryString = "http://www.macvendorlookup.com/api/v2/";

        Node(String ip, String mac){
            this.ip = ip;
            this.mac = mac;
            queryMacVendorLookup();
        }

        @Override
        public String toString() {
            return "IP: " + ip + "\n" + "MAC: " + mac + "\n" + company + "\n" + remark;
        }

        private String sendQuery(String qMac) throws IOException{
            String result = "";

            URL searchURL = new URL(queryString + qMac);

            HttpURLConnection httpURLConnection = (HttpURLConnection) searchURL.openConnection();

            if(httpURLConnection.getResponseCode() == HttpURLConnection.HTTP_OK){
                InputStreamReader inputStreamReader = new InputStreamReader(httpURLConnection.getInputStream());
                BufferedReader bufferedReader = new BufferedReader(
                        inputStreamReader,
                        8192);

                String line = null;
                while((line = bufferedReader.readLine()) != null){
                    result += line;
                }

                bufferedReader.close();
            }

            return result;
        }


        private void ParseResult(String json){

            try {
                JSONArray jsonArray = new JSONArray(json);
                JSONObject jsonObject = (JSONObject) jsonArray.get(0);
                startHex = jsonObject.getString("startHex");
                endHex = jsonObject.getString("endHex");
                startDec = jsonObject.getString("startDec");
                endDec = jsonObject.getString("endDec");
                company = jsonObject.getString("company");
                addressL1 = jsonObject.getString("addressL1");
                addressL2 = jsonObject.getString("addressL2");
                addressL3 = jsonObject.getString("addressL3");
                country = jsonObject.getString("country");
                type = jsonObject.getString("type");
                remark = "OK";

            } catch (JSONException e) {
                e.printStackTrace();
                remark = e.getMessage();
            }

        }

        private void queryMacVendorLookup(){
            try {
                jsonBody = sendQuery(mac);
                ParseResult(jsonBody);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private class TaskReadAddresses extends AsyncTask<Void, Node, Void> {

        ArrayList<Node> array;
        ListView listView;

        TaskReadAddresses(ArrayList<Node> array, ListView v){
            listView = v;
            this.array = array;
            array.clear();
            textResult.setText("querying...");
        }

        @Override
        protected Void doInBackground(Void... params) {
            readAddresses();

            return null;
        }

        @Override
        protected void onPostExecute(Void aVoid) {
            textResult.setText("Done");
        }

        @Override
        protected void onProgressUpdate(Node... values) {
            listNote.add(values[0]);
            ((ArrayAdapter)(listView.getAdapter())).notifyDataSetChanged();

        }

        private void readAddresses() {

            BufferedReader bufferedReader = null;

            try {
                bufferedReader = new BufferedReader(new FileReader("/proc/net/arp"));

                String line;
                while ((line = bufferedReader.readLine()) != null) {
                    String[] splitted = line.split(" +");
                    if (splitted != null && splitted.length >= 4) {
                        String ip = splitted[0];
                        String mac = splitted[3];
                        if (mac.matches("..:..:..:..:..:..")) {
                            Node thisNode = new Node(ip, mac);
                            publishProgress(thisNode);
                        }
                    }
                }

            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally{
                try {
                    bufferedReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}


layout/activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    android:orientation="vertical"
    tools:context="com.blogspot.android_er.androidlistclient.MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:autoLink="web"
        android:text="http://android-er.blogspot.com/"
        android:textStyle="bold" />

    <Button
        android:id="@+id/readclient"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textAllCaps="false"
        android:text="Read Ip/MAC addresses"/>

    <TextView
        android:id="@+id/result"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

    <ListView
        android:id="@+id/nodelist"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
</LinearLayout>


uses-permission of "android.permission.INTERNET" is needed in AndroidManifest.xml

download filesDownload runnable APK .

Next:
Get HostName of WiFi hotspot clients, and check if it is still connected.

Retrieve IP and MAC addresses from /proc/net/arp

Last post "Display WiFi Hotspot clients by "cat /proc/net/arp"" display arp as human readable string, but not machine readable. It's another version to retrieve IP and MAC addresses, store in a ArrayList.


MainActivity.java
package com.blogspot.android_er.androidlistclient;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

    Button btnRead;
    TextView textResult;

    ArrayList<Node> listNote;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btnRead = (Button)findViewById(R.id.readclient);
        textResult = (TextView)findViewById(R.id.result);

        listNote = new ArrayList<>();

        btnRead.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                readAddresses();
                textResult.setText("");
                for(int i=0; i<listNote.size(); i++){
                    textResult.append(i + " ");
                    textResult.append(listNote.get(i).toString());
                    textResult.append("\n");
                }
            }
        });
    }

    private void readAddresses() {
        listNote.clear();
        BufferedReader bufferedReader = null;

        try {
            bufferedReader = new BufferedReader(new FileReader("/proc/net/arp"));

            String line;
            while ((line = bufferedReader.readLine()) != null) {
                String[] splitted = line.split(" +");
                if (splitted != null && splitted.length >= 4) {
                    String ip = splitted[0];
                    String mac = splitted[3];
                    if (mac.matches("..:..:..:..:..:..")) {
                        Node thisNode = new Node(ip, mac);
                        listNote.add(thisNode);
                    }
                }
            }

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally{
            try {
                bufferedReader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    class Node {
        String ip;
        String mac;

        Node(String ip, String mac){
            this.ip = ip;
            this.mac = mac;
        }

        @Override
        public String toString() {
            return ip + " " + mac;
        }
    }
}


layout/activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    android:orientation="vertical"
    tools:context="com.blogspot.android_er.androidlistclient.MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:autoLink="web"
        android:text="http://android-er.blogspot.com/"
        android:textStyle="bold" />

    <Button
        android:id="@+id/readclient"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textAllCaps="false"
        android:text="Read Ip/MAC addresses"/>

    <TextView
        android:id="@+id/result"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:typeface="monospace"
        android:textSize="24sp"/>
</LinearLayout>


Next:
Lookup manufacturer info by MAC address, using www.macvendorlookup.com API
Get HostName of WiFi hotspot clients, and check if it is still connected

Display WiFi Hotspot clients by "cat /proc/net/arp"

This example display WiFi Hotspot connected clients (with IP and MAC address) by Linux command "cat /proc/net/arp", using ProcessBuilder.

(You can download runnable APK from link on bottom)

Tethering OFF, as WiFi client.

Tethering ON, with no client connected.

Tethering ON, with two clients connected; Nexus 7 and Raspberry Pi + WiFi dongle.

But, after any client disconnected, the /proc/net/arp will not updated immediately; I don't know how long it will refresh. One way to update arp is turn OFF and ON tethering again.

Tested on unrooted Xiaomi Redmi 2 running Android 4.4.4.

MainActivity.java
package com.blogspot.android_er.androidlistclient;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import java.io.IOException;
import java.io.InputStream;

public class MainActivity extends AppCompatActivity {

    Button btnRead;
    TextView textResult;

    //String[] args = {"/system/bin/cat", "/proc/net/arp"};
    String[] args = {"cat", "/proc/net/arp"};

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btnRead = (Button)findViewById(R.id.readclient);
        textResult = (TextView)findViewById(R.id.result);

        btnRead.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                textResult.setText(toRead());
            }
        });
    }

    private String toRead()
    {
        ProcessBuilder cmd;
        String result="";

        try{
            cmd = new ProcessBuilder(args);

            Process process = cmd.start();
            InputStream in = process.getInputStream();
            byte[] re = new byte[1024];
            while(in.read(re) != -1){
                System.out.println(new String(re));
                result = result + new String(re);
            }
            in.close();
        } catch(IOException ex){
            ex.printStackTrace();
        }
        return result;
    }
}


layout/activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    android:orientation="vertical"
    tools:context="com.blogspot.android_er.androidlistclient.MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:autoLink="web"
        android:text="http://android-er.blogspot.com/"
        android:textStyle="bold" />
    
    <Button
        android:id="@+id/readclient"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textAllCaps="false"
        android:text="Read /proc/net/arp"/>

    <TextView
        android:id="@+id/result"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:typeface="monospace"
        android:textSize="12sp"/>
</LinearLayout>


download filesDownload runnable APK .

This example display arp in human readable format, but not for machine. Next post "Retrieve IP and MAC addresses from /proc/net/arp" get it in more machine readable.

Sunday, December 13, 2015

Android example to list NetworkInterface



MainActivity.java
package com.blogspot.android_er.androidscannetworkinterface;

import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.Toast;

import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Enumeration;

public class MainActivity extends AppCompatActivity {

    private Button btnScan;
    private ListView listViewNI;

    ArrayList<String> niList;
    ArrayAdapter<String> adapter;

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

        btnScan = (Button)findViewById(R.id.scan);
        listViewNI = (ListView)findViewById(R.id.listviewni);


        niList = new ArrayList();
        adapter = new ArrayAdapter<String>(this,
                android.R.layout.simple_list_item_1, android.R.id.text1, niList);
        listViewNI.setAdapter(adapter);

        btnScan.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new ScanNITask().execute();
            }
        });
    }


    private class ScanNITask extends AsyncTask<Void, String, Void> {

        @Override
        protected void onPreExecute() {
            niList.clear();
            adapter.notifyDataSetInvalidated();
        }

        @Override
        protected Void doInBackground(Void... params) {

            try {
                Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
                while (networkInterfaces.hasMoreElements()) {

                    NetworkInterface thisInterface = networkInterfaces.nextElement();
                    Enumeration<InetAddress> inetAddresses = thisInterface.getInetAddresses();
                    if (inetAddresses.hasMoreElements()) {
                        String niInfo = thisInterface.getDisplayName() + "\n";

                        while (inetAddresses.hasMoreElements()) {
                            InetAddress thisAddress = inetAddresses.nextElement();
                            niInfo += "---\n";
                            niInfo += "Address: " + thisAddress.getAddress() + "\n";
                            niInfo += "CanonicalHostName: " + thisAddress.getCanonicalHostName() + "\n";
                            niInfo += "HostAddress: " + thisAddress.getHostAddress() + "\n";
                            niInfo += "HostName: " + thisAddress.getHostName() + "\n";
                        }

                        publishProgress(niInfo);
                    }


                }
            } catch (SocketException e) {
                e.printStackTrace();
            }

            return null;
        }

        @Override
        protected void onProgressUpdate(String... values) {
            niList.add(values[0]);
            adapter.notifyDataSetInvalidated();

        }

        @Override
        protected void onPostExecute(Void aVoid) {
            Toast.makeText(MainActivity.this, "Done", Toast.LENGTH_LONG).show();
        }
    }
}


activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:autoLink="web"
        android:text="http://android-er.blogspot.com/"
        android:textStyle="bold" />

    <Button
        android:id="@+id/scan"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Scan"/>
    <ListView
        android:id="@+id/listviewni"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
</LinearLayout>


Permission of "android.permission.INTERNET" is needed in AndroidManifest.xml

Thursday, November 26, 2015

socket.getInetAddress() return null on Android 5

Refer to last example of "Simple HTTP server running on Android", it work on Xiaomi Redmi 2 running Android 4.4.4. But somebody report program stopped on Android 5 devices. So I re-test on Nexus 7 running Android 5.1.1. Yes, the app crash! caused by NullPointerException, because socket.getInetAddress() return null.


Modify MainActivity.java to capture socket.getInetAddress() before and after close():
package com.blogspot.android_er.androidhttpserver;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.EditText;
import android.widget.TextView;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.Enumeration;

public class MainActivity extends AppCompatActivity {

    EditText welcomeMsg;
    TextView infoIp;
    TextView infoMsg;
    String msgLog = "";

    ServerSocket httpServerSocket;

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

        welcomeMsg = (EditText) findViewById(R.id.welcomemsg);
        infoIp = (TextView) findViewById(R.id.infoip);
        infoMsg = (TextView) findViewById(R.id.msg);

        infoIp.setText(getIpAddress() + ":"
                + HttpServerThread.HttpServerPORT + "\n");

        HttpServerThread httpServerThread = new HttpServerThread();
        httpServerThread.start();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        if (httpServerSocket != null) {
            try {
                httpServerSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private String getIpAddress() {
        String ip = "";
        try {
            Enumeration<NetworkInterface> enumNetworkInterfaces = NetworkInterface
                    .getNetworkInterfaces();
            while (enumNetworkInterfaces.hasMoreElements()) {
                NetworkInterface networkInterface = enumNetworkInterfaces
                        .nextElement();
                Enumeration<InetAddress> enumInetAddress = networkInterface
                        .getInetAddresses();
                while (enumInetAddress.hasMoreElements()) {
                    InetAddress inetAddress = enumInetAddress.nextElement();

                    if (inetAddress.isSiteLocalAddress()) {
                        ip += "SiteLocalAddress: "
                                + inetAddress.getHostAddress() + "\n";
                    }

                }

            }

        } catch (SocketException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            ip += "Something Wrong! " + e.toString() + "\n";
        }

        return ip;
    }

    private class HttpServerThread extends Thread {

        static final int HttpServerPORT = 8888;

        @Override
        public void run() {
            Socket socket = null;

            try {
                httpServerSocket = new ServerSocket(HttpServerPORT);

                while(true){
                    socket = httpServerSocket.accept();

                    HttpResponseThread httpResponseThread =
                            new HttpResponseThread(
                                    socket,
                                    welcomeMsg.getText().toString());
                    httpResponseThread.start();
                }
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

    }

    private class HttpResponseThread extends Thread {

        Socket socket;
        String h1;

        HttpResponseThread(Socket socket, String msg){
            this.socket = socket;
            h1 = msg;
        }

        @Override
        public void run() {
            BufferedReader is;
            PrintWriter os;
            String request;

            try {
                is = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                request = is.readLine();

                os = new PrintWriter(socket.getOutputStream(), true);

                String response =
                        "<html><head></head>" +
                                "<body>" +
                                "<h1>" + h1 + "</h1>" +
                                "</body></html>";

                os.print("HTTP/1.0 200" + "\r\n");
                os.print("Content type: text/html" + "\r\n");
                os.print("Content length: " + response.length() + "\r\n");
                os.print("\r\n");
                os.print(response + "\r\n");
                os.flush();
                InetAddress clientInetAddressBeforeClose = socket.getInetAddress();
                socket.close();
                InetAddress clientInetAddressAfterClose = socket.getInetAddress();

                msgLog += "Request: " + request + "\n";

                if(clientInetAddressBeforeClose == null){
                    msgLog += "clientInetAddressBeforeClose == null\n";
                }else{
                    msgLog += "clientInetAddressBeforeClose = " + clientInetAddressBeforeClose.toString() + "\n";
                }

                if(clientInetAddressAfterClose == null){
                    msgLog += "clientInetAddressAfterClose == null\n";
                }else{
                    msgLog += "clientInetAddressAfterClose = " + clientInetAddressAfterClose.toString() + "\n";
                }

                MainActivity.this.runOnUiThread(new Runnable() {

                    @Override
                    public void run() {

                        infoMsg.setText(msgLog);
                    }
                });

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

            return;
        }
    }
}


It's found that on Xiaomi Redmi 2 running Android 4.4.4, socket.getInetAddress() keep no change before and after close().


But on Nexus 7 running Android 5.1.1, change to null after close().


So, we have to read socket.getInetAddress() before close().