Android Socket Connectio Refused - android

I am having trouble connecting two android phones via socket i am using android debug bridge to connect the phones to my PC from which i can launch the emulator on the phones.
I launch the server on one phone and try to connect with the other however i get the following error on my client side:
W/System.err: java.net.ConnectException: failed to connect to /192.168.49.1 (port 8080): connect failed: ECONNREFUSED
Server
private class SocketServerThread extends Thread {
static final int SocketServerPORT = 8080;
#Override
public void run() {
try {
serverSocket = new ServerSocket(SocketServerPORT);
Log.d("Quiz", "Server creation connection success");
Log.d("Quiz", "Server local port "+serverSocket.getLocalPort());
Log.d("Quiz", "Server local port "+serverSocket.getInetAddress());
HostingScreen.this.runOnUiThread(new Runnable() {
#Override
public void run() {
}
});
while (true) {
Socket socket = serverSocket.accept();
count++;
message += "#" + count + " from " + socket.getInetAddress()
+ ":" + socket.getPort() + "\n";
HostingScreen.this.runOnUiThread(new Runnable() {
#Override
public void run() {
msg.setText(message);
}
});
SocketServerReplyThread socketServerReplyThread = new SocketServerReplyThread(
socket, count);
socketServerReplyThread.run();
}
} catch (IOException e) {
e.printStackTrace();
}
}
Client
#Override
protected Object doInBackground(Object[] params) {
Socket socket = null;
try {
socket = new Socket(host,port);
Log.d("Quiz", "Client socket success");
connected = true;
while (connected) {
try {
Log.d("Quiz", "Sending command");
PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket
.getOutputStream())), true);
// where you issue the commands
} catch (Exception e) {
}
}
socket.close();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
if(socket != null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
The port is hard coded to port 8080 for both the server and the client for the time being, the host address is found when i make a connection between the phones by using the following code:
Connection
private void onConnectionChanged(Intent intent) {
final NetworkInfo netInfo = intent.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
final WifiP2pInfo p2pInfo = intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO);
isConnected = netInfo.isConnected();
if (isConnected) {
//Get host connection info
wifiMgr.requestConnectionInfo(channel, new ConnectionInfoListener() {
#Override
public void onConnectionInfoAvailable(final WifiP2pInfo info) {
wifiMgr.requestGroupInfo(channel, new GroupInfoListener() {
#Override
public void onGroupInfoAvailable(WifiP2pGroup group) {
if (group == null)
return;
WiFiP2P.this.group = group;
Log.d(TAG, "Wifi p2p connection group is " + group.getNetworkName());
Log.d(TAG, "Group size " + group.getClientList().size());
fireOnConnectionSucceed(group.getNetworkName(), group.getPassphrase());
//create client if not host
if(info.isGroupOwner) {
Client client = new Client(8080, p2pInfo.groupOwnerAddress.getHostAddress());
client.execute();
Log.d(TAG, "Client launch success");
Log.d(TAG, "Host address " + p2pInfo.groupOwnerAddress.getHostAddress());
}
}
});
if (isConnected && !info.isGroupOwner) {
} else {
startDiscovery();
}
}
});
} else {
group = null;
fireOnConnectionLost();
}
}
I check to see if the person connected is the host or not, if not i launch the client passing the port and host address to the client which is an Aysnc task where the socket to connect is created.
The error occurs in the client when
socket = new Socket(host,port);
is used causing the error stated.
Any ideas as to what the problem could be? They both connect to each other over WiFi but when i try to connect to the server socket it fails.
Thanks in advance.
Edit:
To clear somethings up, i use the adb to get the app from my computer onto my phone from which i can launch the app.
I am connecting both phones using wifimanager i need to find which of the phones created the wifi group and then that person is the host of the server to which i can connect the problem arises when i try to launch a socket to connect to the host using the host address from the connect info.

I managed to solve my problem, it turns out the device which was hosting the server wasn't being designated the group owner.
config.groupOwnerIntent=0;
Setting the owner intent of connecting devices to 0 fixed the problem.

Related

Serversocket.accept() not accepting connection in server side when implemented in a background service

I am trying to establish a client server socket connection with wifi direct using the demo WiFi direct app. The demo app works fine where the connection is being established when the activity is open but
if I create a background service and try to listen for socket connections, the server running in the background service does not accept connections.
The code is as given below:
Server:
public class MyBackgroundService extends Service implements ChannelListener, ConnectionInfoListener {
private Runnable runnable = new Runnable() {
#Override
public void run() {
// put your socket-code here
String filename= null;
// Toast t = Toast.makeText(context,"Opening a server socket",Toast.LENGTH_LONG);
// t.show();
DataInputStream inputstream = null;
try {
ServerSocket serverSocket = new ServerSocket(8988);
Log.e(WiFiDirectActivity.TAG, "Server: Socket opened");
Socket client = serverSocket.accept();
Log.e(WiFiDirectActivity.TAG, "Server: connection done");
....
}
#Override
public void onConnectionInfoAvailable(final WifiP2pInfo info) {
this.info = info;
if (info.groupFormed && info.isGroupOwner) {
Log.e(WiFiDirectActivity.TAG,"FileServeronConneitoninfo Background");
new Thread(runnable).start();
}
}
Client:
public class FileTransferService extends IntentService {
private static final int SOCKET_TIMEOUT = 0;
#Override
protected void onHandleIntent(Intent intent) {
Context context = getApplicationContext();
if (intent.getAction().equals(ACTION_SEND_FILE)) {
String filename= intent.getExtras().getString(EXTRAS_FILE_PATH);
String host = intent.getExtras().getString(EXTRAS_GROUP_OWNER_ADDRESS);
Socket socket = new Socket();
int port = intent.getExtras().getInt(EXTRAS_GROUP_OWNER_PORT);
DataOutputStream stream = null;
try {
Log.e(WiFiDirectActivity.TAG, "Opening client socket - ");
socket.bind(null);
Log.e(WiFiDirectActivity.TAG, "Opening client socket - ");
socket.connect((new InetSocketAddress(host, port)), SOCKET_TIMEOUT);
Log.e(WiFiDirectActivity.TAG, "Client socket - " + socket.isConnected());
// ...
}
catch (IOException e) {
Log.e(WiFiDirectActivity.TAG, "IOException in File transfer");
Log.e(WiFiDirectActivity.TAG, e.getMessage());
}
finally {
if (socket != null) {
if (socket.isConnected()) {
try {
socket.close();
stream.close();
} catch (IOException e) {
// Give up
e.printStackTrace();
}
}
}
}
The client side is getting timeout exception and both the lines after the connect and accept are not executing. Is there any possible reason?
I have put timeout 0 to prevent timeout exception but still there are no connections established. This is happening only in the background service but if I do this in activity everything works fine.
Thanks in advance.
Log Cat Output of server side:
01-28 11:21:12.391 E/wifidirectdemo: COnnection_Change_BackgroundService
01-28 11:21:12.392 E/wifidirectdemo: ConnectionInfo Available Background
01-28 11:21:12.392 E/wifidirectdemo: FileServeronConneitoninfo Background
01-28 11:21:12.394 E/wifidirectdemo: Server: Socket opened
01-28 11:21:22.877 E/wifidirectdemo: P2P peers changed

Why can't I connect through socket

I'm making an android app that connects with PC. I'm using solution that I've found HERE
When I try to connect with PC by giving exact IP adress everything works fine. Phone connects fast with PC or (when server on PC isn't running) I get info about unability to connect fast. Here is code:
public class ConnectPhoneTask extends AsyncTask<String,Void,Boolean> {
#Override
protected Boolean doInBackground(String... params) {
boolean result = true;
try {
InetAddress serverAddr = InetAddress.getByName(params[0]);
socket = new Socket(serverAddr, Constants.SERVER_PORT);//Open socket on server IP and port
} catch (IOException e) {
Log.e("remotedroid", "Error while connecting", e);
result = false;
}
return result;
}
#Override
protected void onPostExecute(Boolean result)
{
isConnected = result;
Toast.makeText(context,isConnected?"Connected to server!":"Error while connecting",Toast.LENGTH_LONG).show();
try {
if(isConnected) {
out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket
.getOutputStream())), true); //create output stream to send data to server
}
}catch (IOException e){
Log.e("remotedroid", "Error while creating OutWriter", e);
Toast.makeText(context,"Error while connecting",Toast.LENGTH_LONG).show();
}
}
}
But when I try to loop through IP adresses to find device that I am able to connect to it takes very long time, until timeout. Here is code:
public class DevicesListTask extends AsyncTask<String,Void,List<Device>>
{
#Override
protected void onPreExecute() {
pb.setVisibility(ProgressBar.VISIBLE);
}
#Override
protected List<Device> doInBackground(String... params) {
List<Device> devices=new ArrayList<Device>();
String device_ip;
Socket socket;
for(int i=0;i<4;i++)
{
device_ip=params[0]+Integer.toString(i);
try {
InetAddress serverAddr = InetAddress.getByName(device_ip);
socket = new Socket(serverAddr, 8988);
devices.add(new Device(device_ip,socket.getInetAddress().getHostName()));
} catch (IOException e) {
Log.e("remotedroid", "Error while connecting", e);
}
}
return devices;
}
#Override
protected void onPostExecute(List<Device> devices) {
pb.setVisibility(ProgressBar.INVISIBLE);
if(devices!=null)
{
Intent intent = new Intent(context,DevicesList.class);
String[] devicesIPS = new String[devices.size()];
String[] devicesNames = new String[devices.size()];
for(int i=0;i<devices.size();i++)
{
devicesIPS[i]=devices.get(i).getIP();
devicesNames[i]=devices.get(i).getName();
}
intent.putExtra("DEVICES_IPS",devicesIPS);
intent.putExtra("DEVICES_NAMES",devicesNames);
startActivity(intent);
}
else
{
Toast.makeText(context,"nope",Toast.LENGTH_LONG).show();
}
}
}
I just change a little bit code from example I linked above. What is wrong with this code?
Exception I get is:
java.net.ConnectException: failed to connect to /192.168.1.2 (port 8988): connect failed: ETIMEDOUT (Connection timed out)
I get it while trying to connect to my PC, but I get this exception only while looping through adresses and not in first example. While using code from first example i get connected instantly. What's wrong wit that second bit of code that connection times out?
You will get connect timeouts when you try to connect to IP addresses that don't exist. The default timeout is around a minute. If you're getting connect timeouts you can shorten them as follows:
Socket socket = new Socket(); // create an unconnected socket
int timeout = 5000; // in milliseconds, tune as required
socket.connect(new InetSocketAddress(serverAddr, 8989), timeout);
5 seconds is more than enough in most circumstances, you can work it down to 2-3 seconds, not less.
Did you set the internet permissions in the Manifest file for the app? If not, then Android is sock-blocking you.

JSR82 connect: Connection to the Bluetooth socket is not created (failed or aborted)

So there are many SO posts related to this issue -
java.io.IOException: [JSR82] accept: Connection is not created (failed or aborted)
java.io.IOException: [JSR82] while connecting to a bluetooth device
Other Posts -
http://android.2317887.n4.nabble.com/getting-exception-while-connecting-to-server-via-bluetooth-td11390.html
http://www.coderexception.com/Cbz1B31bPPQixWQQ/connection-is-not-created-failed-or-aborted-exception-thrown-during-bluetooth-connection-in-android
Tried everything from reflection to without reflection but none worked -
if(!mDeviceAddress.equals("") && BluetoothAdapter.checkBluetoothAddress(mDeviceAddress))
{
Log.i(TAG, "Remote Device Name "+mDeviceName);
bdDevice = mBluetoothAdapter.getRemoteDevice(mDeviceAddress);
getConnected(bdDevice);
}
#TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
public void getConnected(BluetoothDevice bdDevice)
{
if(bdDevice == null)
{
setSetting("STATUS", "Disconnected");
Toast.makeText(getActivity(),
"Unable to get Remote Device!", Toast.LENGTH_SHORT).show();
return;
}
else
{
Log.i(TAG, "Connecting Address--"+ bdDevice.getAddress());
boolean isConnected = createInsecureRfcommSocket(bdDevice, 1);
if(!isConnected)
{
for(int i=2;i<4;i++)
{
if(!isConnected)
isConnected = createInsecureRfcommSocket(bdDevice, i);
else
break;
}
}
if(isConnected)
{
Log.i(TAG, "Connected Socket");
setSetting("STATUS", "Connected");
mConnectedThread = new ConnectedThread(socket);
mConnectedThread.start();
timeSyncCommand();
mConnectedThread.writeByte(runCommand);
startTime = System.currentTimeMillis();
}
else
{
try
{
socket = bdDevice.
createInsecureRfcommSocketToServiceRecord(
UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
}
catch(IOException io)
{
Toast.makeText(getActivity(), "Socket Create -"
+ io.toString() , Toast.LENGTH_SHORT).show();
}
try
{
mBluetoothAdapter.cancelDiscovery();
socket.connect();
}
catch(IOException io)
{
Log.i(TAG, "Socket Connect -"+io.toString());
}
if(socket.isConnected())
{
Log.i(TAG, "Connected Socket");
setSetting("STATUS", "Connected");
mConnectedThread = new ConnectedThread(socket);
mConnectedThread.start();
timeSyncCommand();
mConnectedThread.writeByte(runCommand);
startTime = System.currentTimeMillis();
}
else
{
Log.i(TAG, "Disconnected Socket");
setSetting("STATUS", "Disconnected");
}
}
}
}
#TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
public boolean createInsecureRfcommSocket(BluetoothDevice bdDevice, int i)
{
try
{
Log.i(TAG,
"Creating RFCOMM socket using reflection with Object "+i);
//socket = bdDevice.createRfcommSocketToServiceRecord(my_UUID);
Method m = bdDevice.getClass().
getMethod("createInsecureRfcommSocket", new Class[] {int.class});
socket = (BluetoothSocket) m.invoke(bdDevice, i);
mBluetoothAdapter.cancelDiscovery();
Log.i(TAG,"Attempt to connect to a remote device");
socket.connect();
}
catch(IOException e)
{
setSetting("STATUS", "Disconnected");
Log.i(TAG,"Exception raised "+e.getMessage());
try
{
socket.close();
Log.i(TAG,
"Cannot connect with address "+bdDevice.getAddress());
e.printStackTrace();
}
catch (IOException e1)
{
Log.i(TAG,"Socket not closed");
e1.printStackTrace();
}
}
catch (NoSuchMethodException e1)
{
Log.i(TAG,"NoSuchMethodException");
e1.printStackTrace();
}
catch (InvocationTargetException e2)
{
Log.i(TAG,"InvocationTargetException");
e2.printStackTrace();
}
catch (IllegalAccessException e3)
{
Log.i(TAG,"IllegalAccessException");
e3.printStackTrace();
}
catch (NullPointerException e4)
{
Log.i(TAG,"NullPointerException");
e4.printStackTrace();
}
}
If you see carefully to the above code then you notice -
1) Tried reflection with port 1
2) If 1 fails then tried reflection with port 2
3) If 2 fails then tried reflection with port 3
4) If 3 fails then tried without reflection
In short, used everything but none worked.
My exception trace -
Remote Device Name RN-IAP-E281
Connecting Address--00:06:68:4D:E2:81
Creating RFCOMM socket using reflection with Object 1
Attempt to connect to a remote device
Exception raised [JSR82] connect: Connection is not created (failed or aborted).
Cannot connect with address 00:06:68:4D:E2:81
Creating RFCOMM socket using reflection with Object 2
Attempt to connect to a remote device
Exception raised [JSR82] connect: Connection is not created (failed or aborted).
Cannot connect with address 00:06:68:4D:E2:81
Creating RFCOMM socket using reflection with Object 3
Attempt to connect to a remote device
Exception raised [JSR82] connect: Connection is not created (failed or aborted).
Cannot connect with address 00:06:68:4D:E2:81
Disconnected Socket
------onReceive BroadcastReceiver------
Received Bluetooth Disconnected Request
------Returned from broadcast after disconnect------
Any help will be appreciated!

TCP hole punching, can't reuse address on android

I have an android device hosting an HTTP server and is NATed(behind router). I want to access it from a public server which is not NATed.
I run a HTTP server on android device at port 8080 in a thread and after some time, I launch a client thread which tries to connect to remote public server from local port number 8080 on android.
For some reason, android client won't connect after I start server on port 8080. Nor does it yield any exception. Here is my code:
Thread server = new Thread(new Runnable() {
public void run() {
super.run();
try {
ServerSocket serverSocket = new ServerSocket();
serverSocket.setReuseAddress(true);
serverSocket.bind(new InetSocketAddress("192.168.1.102", 8080));
while(isRunning){
try {
System.out.println("Listening for new connection");
Socket socket = serverSocket.accept();
System.out.println("incoming connection accepted");
new Thread(new ConnectionHandler(socket)).start();
} catch (IOException e) {
e.printStackTrace();
}
}
serverSocket.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
});
server.start();
// sleep several seconds before launch of client
Thread.currentThread().sleep(5 * 1000);
Thread client = new Thread(new Runnable() {
public void run() {
super.run();
try {
/* .. */
Inet4Address localaddr = (Inet4Address) InetAddress
.getByName("192.168.1.102");
// Inet4Address remoteaddr = (Inet4Address)
// InetAddress.getByName("122.176.73.10");
System.out.println("connecting to 122.176.73.10");
Socket socket = new Socket();
socket.setReuseAddress(true);
System.out.println("[Client]socket.isBound():" + socket.isBound());
socket.bind(new InetSocketAddress("192.168.1.102", 8080));
for (int i = 1; i < 5; i++) {
try {
socket.connect(new InetSocketAddress("122.176.73.10", 4040));
System.out.println("connected to 122.176.73.10");
break;
} catch (Exception e) {
System.out.println("[Client]fail to connect ");
Thread.currentThread().sleep(i * 2 * 1000);
}
}
}
}
});
client.start();
A port number can't be used to both listen for incoming connections and make an outgoing connection. Your listening socket on 8080 prevents anything else using that port.

how to connect and send a message to multiple devices through blue tooth in android?

I would like to develop an application for sending a message to multiple devices through blue tooth.I know blue tooth is a point to point communication even though i would like to connect and send a message as following steps:
1.Get the list of paired devices
2.Select a device from paired list
3.Connect to paired device , send a message to selected paired device
4.Disconnect from device
5.Get connection to another device and so on (one after another).
I am getting paired devices addresses list as follows:
mBtAdapter = BluetoothAdapter.getDefaultAdapter();
Set<BluetoothDevice> pairedDevices = mBtAdapter.getBondedDevices();
if (pairedDevices.size() > 0) {
for (BluetoothDevice device : pairedDevices) {
pairedList.add(device.getAddress());
}
Log.v("11111111", "11111111111"+dev);
}
I am trying to connect to them and sending a message when user clicks on a button as follows:
((Button)findViewById(R.id.button1)).setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
String message = "Haiii";
for(int i=0;i<dev.size();i++){
Log.v("device", "111111 : "+pairedList.get(i));
mbService.connect(mBtAdapter.getRemoteDevice(pairedList.get(i)));
mbService.write(message.getBytes());
mbService.stop();
}
}
});
From the above code i am getting connection when loop pairedList.get(0).But the message is not sending to another device.In another device api sample application has installed.
If I use pairedList.get(i) it is not connecting to any devices even single device also.
please help me .
try to create separate threads for each connection - I had a similar issue and creating a new thread for each connection solved it nicely. By the way I even create a new thread to establish the connection - so establishing the connection does not block the UI. Got this from the BT sample code...
to create a new thread to establish a connection:
mConnectBluetoothThread = new ConnectBluetoothThread(device);
mConnectBluetoothThread.start();
where ConnectBluetoothThread is defined like:
public ConnectBluetoothThread(BluetoothDevice device) {
if (DEBUG)
Log.i(this.getClass().getSimpleName(),
this.getClass().getName()
+ " ->"
+ Thread.currentThread().getStackTrace()[2]
.getMethodName());
mmDevice = device;
BluetoothSocket tmp = null;
// Get a BluetoothSocket for a connection with the
// given BluetoothDevice
try {
tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
} catch (IOException e) {
Log.e(this.getClass().getSimpleName(), "create() failed", e);
}
mmSocket = tmp;
}
public void run() {
if (DEBUG)
Log.i(this.getClass().getSimpleName(),
this.getClass().getName()
+ " ->"
+ Thread.currentThread().getStackTrace()[2]
.getMethodName());
// TODO
setName("ConnectThread");
// Always cancel discovery because it will slow down a connection
mBluetoothAdapter.cancelDiscovery();
// Make a connection to the BluetoothSocket
try {
// This is a blocking call and will only return on a
// successful connection or an exception
mmSocket.connect();
} catch (IOException e) {
connectionFailed();
// Close the socket
try {
mmSocket.close();
} catch (IOException e2) {
Log.e(this.getClass().getSimpleName(),
"unable to close() socket during connection failure",
e2);
}
return;
}
// Reset the ConnectThread because we're done
synchronized (InterBT.this) {
mConnectBluetoothThread = null;
}
// Start the connected thread
connected(mmSocket, mmDevice);
}
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) {
Log.e(this.getClass().getSimpleName(),
"close() of connect socket failed", e);
}
}
}
public synchronized void connected(BluetoothSocket socket,
BluetoothDevice device) {
if (DEBUG)
Log.d(this.getClass().getSimpleName(), "connected");
// Cancel the thread that completed the connection
if (mConnectBluetoothThread != null) {
mConnectBluetoothThread.cancel();
mConnectBluetoothThread = null;
}
// Cancel any thread currently running a connection
if (mConnectedBluetoothThread != null) {
mConnectedBluetoothThread.cancel();
mConnectedBluetoothThread = null;
}
// Cancel the accept thread because we only want to connect to one
// device
// if (mAcceptThread != null) {mAcceptThread.cancel(); mAcceptThread =
// null;}
// Start the thread to manage the connection and perform transmissions
mConnectedBluetoothThread = new ConnectionThreadBT(socket);
mConnectedBluetoothThread.start();
setState(STATE_CONNECTED);
}
and also create a new class ConnectionThreadBT that handles the connection to read and write:
public class ConnectionThreadBT extends ConnectionThreadBase {
private static final boolean DEBUG = true;
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;
byte[] responseBuffer = new byte[4096 * 4];
int responseBufferLen = 0;
public ConnectionThreadBT(BluetoothSocket socket) {
if (DEBUG)
Log.i(this.getClass().getSimpleName(),
this.getClass().getName()
+ " ->"
+ Thread.currentThread().getStackTrace()[2]
.getMethodName());
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;
// Get the BluetoothSocket input and output streams
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) {
Log.e(this.getClass().getSimpleName(), "temp sockets not created",
e);
}
mmInStream = tmpIn;
mmOutStream = tmpOut;
}
public void run() {
if (DEBUG)
Log.i(this.getClass().getSimpleName(),
this.getClass().getName()
+ " ->"
+ Thread.currentThread().getStackTrace()[2]
.getMethodName());
//we have successfully connected to BT
//now inform UI
Home_Screen.sendMessageToHomeScreen(
Home_Screen.MESSAGE_INTERBT_CONNECTION_TESTED,
Home_Screen.CONNECTION_SUCCESS, true);
}
and then to write just call this method which is also defined within ConnectionThreadBT
public void sendMsg(MyBuffer buffer){
try {
mmOutStream.write(buffer);
mmOutStream.flush();
successfullyWritten = true;
} catch (IOException e) {
Log.e(this.getClass().getSimpleName(),
"Exception during write", e);
successfullyWritten = false;
}
to read either do the same or start a monitoring loop in the run method which keeps reading as long as the connectedThread is alive and reports back any read information through a handler similar to the UI screen update

Categories

Resources