It is now over a month that I'm trying to send a string using WiFi-Direct between two android devices, but I'm still struggling hard to understand what I'm doing wrong.
I've looked around forums but they often don't give much detail on how to achieve what I want.
I also went through those two guides from the android developer's website:
Create P2P connections with Wi-Fi Direct
Wi-Fi Direct (peer-to-peer or P2P) overview
I'm using one activity - ActivityConnection - where I toggle the visibility of views depending on whether the user previously chose to send or to receive the string.
Immediatly, on the client side, discoverPeers() looks for any device with WiFi-Direct turned on and displays them on a ListView. Once the user chooses a device and presses the send button, the connection makes itself and the string is sent.
On the server side, the server is immediatly launched using my AsyncServerTask class. There, it waits for a client to connect and to retrieve its sent string.
My main problem is that, after choosing the device and tapping on the send button, the server side isn't receiving anything.
My second problem is that, sometimes, devices aren't being discovered and the listview stays empty.
Am I missing something? Or maybe doing something wrong?
Here's my current code.
I took the liberty to get rid of any line I thought to be out of context to make it easier to read.
ActivityConnection
public class ActivityConnection extends AppCompatActivity implements NewPeersListener {
public static final String CONNECTION_ACTOR = "actor";
public static final String SEND_INFO = "send";
public static final String RECEIVE_INFO = "receive";
ListView listViewDevices;
private IntentFilter intentFilter;
private WifiP2pManager manager;
private WifiP2pManager.Channel channel;
private WiFiDirectBroadcastReceiver receiver;
public List <WifiP2pDevice> listDevices;
private WifiP2pDevice selectedDevice;
#Override
public void onCreate (Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
confirm.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
connectToSelectedDevice();
}
});
Intent intent = this.getIntent();
String actor = intent.getStringExtra(CONNECTION_ACTOR);
this.intentFilter = new IntentFilter();
this.intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
this.intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
this.intentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
this.intentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
this.manager = (WifiP2pManager) this.getSystemService(Context.WIFI_P2P_SERVICE);
this.channel = this.manager.initialize(this, this.getMainLooper(), null);
this.receiver = new WiFiDirectBroadcastReceiver(this.manager, this.channel, this);
this.listDevices = new ArrayList <> ();
if (actor.equals(SEND_INFO)) {
DeviceAdapter adapter = new DeviceAdapter(ActivityConnection.this, R.layout.device_item, this.listDevices);
this.listViewDevices.setAdapter(adapter);
this.listViewDevices.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
selectedDevice = listDevices.get(position);
}
});
this.discoverPeers();
}
else if (actor.equals(RECEIVE_INFO)) {
new ServerAsyncTask(this).execute();
}
}
#Override
protected void onResume() {
super.onResume();
this.receiver = new WiFiDirectBroadcastReceiver(this.manager, this.channel, this);
this.registerReceiver(this.receiver, this.intentFilter);
}
#Override
protected void onPause() {
super.onPause();
this.unregisterReceiver(this.receiver);
}
public void resultReceived (String result) {
Toast.makeText(ActivityConnection.this, "Received! :)", Toast.LENGTH_SHORT).show();
}
private void discoverPeers () {
manager.discoverPeers(channel, new WifiP2pManager.ActionListener() {
#Override
public void onSuccess() {
// The discovery process succeeded
}
#Override
public void onFailure(int reason) {
// The discovery process DID NOT succeed
Toast.makeText(ActivityConnection.this, "Discovery process DID NOT succeed. Please verify that WiFi-Direct is active.", Toast.LENGTH_LONG).show();
}
});
}
private void connectToSelectedDevice () {
WifiP2pConfig config = new WifiP2pConfig();
config.deviceAddress = this.selectedDevice.deviceAddress;
this.manager.connect(this.channel, config, new WifiP2pManager.ActionListener() {
#Override
public void onSuccess() {
// Send string
Intent serviceIntent = new Intent(ActivityConnection.this, TransferService.class);
serviceIntent.setAction(TransferService.ACTION_SEND_STRING);
serviceIntent.putExtra(TransferService.EXTRAS_GROUP_OWNER_ADDRESS, getMacAddress());
serviceIntent.putExtra(TransferService.EXTRAS_GROUP_OWNER_PORT, 8090);
startService(serviceIntent);
onBackPressed();
}
#Override
public void onFailure(int reason) {
Toast.makeText(ActivityConnection.this, "Connection failed. Try again.", Toast.LENGTH_SHORT).show();
}
});
}
#NonNull
private String getMacAddress () {
try {
List <NetworkInterface> all = Collections.list(NetworkInterface.getNetworkInterfaces());
for (NetworkInterface nif : all) {
if (!nif.getName().equalsIgnoreCase("wlan0")) continue;
byte[] macBytes = nif.getHardwareAddress();
if (macBytes == null) {
return "";
}
StringBuilder result = new StringBuilder();
for (byte b : macBytes) {
result.append(String.format("%02X:",b));
}
if (result.length() > 0) {
result.deleteCharAt(result.length() - 1);
}
return result.toString();
}
} catch (Exception e) {
}
return "02:00:00:00:00:00";
}
#Override
public void newPeers (WifiP2pDeviceList wifiP2pDeviceList) {
this.listDevices = new ArrayList <> (wifiP2pDeviceList.getDeviceList());
DeviceAdapter adapter = new DeviceAdapter(ActivityConnection.this, R.layout.device_item, this.listDevices);
this.listViewDevices.setAdapter(adapter);
}
}
WiFiDirectBroadcastReceiver
public class WiFiDirectBroadcastReceiver extends BroadcastReceiver {
private WifiP2pManager manager;
private WifiP2pManager.Channel channel;
private ActivityConnection activity;
private List <NewPeersListener> listeners;
public WiFiDirectBroadcastReceiver(WifiP2pManager manager, WifiP2pManager.Channel channel, ActivityConnection activity) {
super();
this.manager = manager;
this.channel = channel;
this.activity = activity;
this.listeners = new ArrayList <> ();
this.listeners.add(activity);
}
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) {
int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1);
if (state == WifiP2pManager.WIFI_P2P_STATE_ENABLED) {
// Wi-Fi P2P is enabled
} else {
// Wi-Fi P2P is not enabled
Toast.makeText(this.activity, "Please turn on WiFi-Direct (or WiFi-P2P).", Toast.LENGTH_SHORT).show();
}
} else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {
// Request available peers from the wifi p2p manager.
if (this.manager != null) {
this.manager.requestPeers(this.channel, new WifiP2pManager.PeerListListener() {
#Override
public void onPeersAvailable(WifiP2pDeviceList peers) {
for (NewPeersListener listener : listeners) {
listener.newPeers(peers);
}
}
});
}
} else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {
// Respond to new connection or disconnections
} else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)) {
// Respond to this device's wifi state changing
}
}
}
ServerAsyncTask (server)
public class ServerAsyncTask extends AsyncTask<Void, Void, String> {
private ServerSocket serverSocket;
private Socket clientSocket;
private DataInputStream stream;
private WeakReference <Context> contextWeakReference;
ServerAsyncTask (Context context) {
this.contextWeakReference = new WeakReference <> (context);
}
#Override
protected String doInBackground (Void... params) {
try {
this.serverSocket = new ServerSocket(8090);
this.clientSocket = this.serverSocket.accept();
this.stream = new DataInputStream(this.clientSocket.getInputStream());
String received = this.stream.readUTF();
this.serverSocket.close();
return received;
} catch (IOException e) {
Log.e(TransferService.TAG, Objects.requireNonNull(e.getMessage()));
return null;
} finally {
if (this.stream != null) {
try {
this.stream.close();
} catch (IOException e) {
Log.e(TransferService.TAG, Objects.requireNonNull(e.getMessage()));
}
}
if (this.clientSocket != null) {
try {
this.clientSocket.close();
} catch (IOException e) {
Log.e(TransferService.TAG, Objects.requireNonNull(e.getMessage()));
}
}
if (this.serverSocket != null) {
try {
this.serverSocket.close();
} catch (IOException e) {
Log.e(TransferService.TAG, Objects.requireNonNull(e.getMessage()));
}
}
}
}
/*
* (non-Javadoc)
* #see android.os.AsyncTask#onPostExecute(java.lang.Object)
*/
#Override
protected void onPostExecute (String result) {
super.onPostExecute(result);
((ActivityConnection) this.contextWeakReference.get()).resultReceived(result);
}
}
TransferService (client)
public class TransferService extends IntentService {
public static final String TAG = "WIFI_DIRECT";
private static final int SOCKET_TIMEOUT = 5000;
public static final String ACTION_SEND_STRING = "sendString";
public static final String EXTRAS_GROUP_OWNER_ADDRESS = "go_host";
public static final String EXTRAS_GROUP_OWNER_PORT = "go_port";
public TransferService (String name) {
super(name);
}
public TransferService () {
super("TransferService");
}
#Override
protected void onHandleIntent (Intent intent) {
Context context = getApplicationContext();
if (intent.getAction().equals(ACTION_SEND_STRING)) {
String toSend = "string to send";
String host = intent.getExtras().getString(EXTRAS_GROUP_OWNER_ADDRESS);
int port = intent.getExtras().getInt(EXTRAS_GROUP_OWNER_PORT);
Socket socket = null;
DataOutputStream stream = null;
try {
// Create a client socket with the host, port, and timeout information.
socket = new Socket();
socket.bind(null);
socket.connect((new InetSocketAddress(host, port)), SOCKET_TIMEOUT);
Log.d(TAG, "Client connected socket - " + socket.isConnected());
// Send string
stream = new DataOutputStream(socket.getOutputStream());
stream.writeUTF(toSend);
stream.close();
Toast.makeText(context, "Sent! :)", Toast.LENGTH_SHORT).show();
} catch (IOException e) {
Log.e(TAG, Objects.requireNonNull(e.getMessage()));
} finally {
if (stream != null) {
try {
stream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (socket != null) {
if (socket.isConnected()) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
}
}
In the ActivityConnetion class, I was giving a MAC address instead of an IP address. I don't know why I didn't see this, nor why I did it in the first place. So I looked around this forum and found out how to get the IP address of the WiFi-Direct's group owner: Wifi Direct Group Owner Address .
To get the code running, I went into the ActivityConnetion class, delete the getMacAddress() method and replaced this line:
serviceIntent.putExtra(TransferService.EXTRAS_GROUP_OWNER_ADDRESS, getMacAddress());
with this line:
serviceIntent.putExtra(TransferService.EXTRAS_GROUP_OWNER_ADDRESS, "192.168.49.1");
As the group owner's IP is always the same, one can write it down directly. As doing so can stop working if the IP changes, I would recommend looking for the group owner's IP instead. The link above show's how to do it.
Related
I have an app which automatically fetch data online whenever it is opened. I would like to make it a way that the app will only check for update online when a blacklisted app is not detected.
This is the update core.
public class UpdateCore extends AsyncTask<String, String, String> {
private static final String TAG = "NetGuard.Download";
private Context context;
private Listener listener;
private PowerManager.WakeLock wakeLock;
private HttpURLConnection uRLConnection;
private InputStream is;
private TorrentDetection torrent;
private BufferedReader buffer;
private String url;
public interface Listener {
void onLoading();
void onCompleted(String config) throws Exception;
void onCancelled();
void onException(String ex);
}
public UpdateCore(Context context, String url, Listener listener) {
this.context = context;
this.url = url;
this.listener = listener;
}
#Override
protected void onPreExecute() {
listener.onLoading();
}
#Override
protected String doInBackground(String... args) {
try {
String api = url;
if(!api.startsWith("http")){
api = new StringBuilder().append("http://").append(url).toString();
}
URL oracle = new URL(api);
HttpClient Client = new DefaultHttpClient();
HttpGet httpget = new HttpGet(oracle.toURI());
HttpResponse response = Client.execute(httpget);
InputStream in = response.getEntity().getContent();
BufferedReader reader = new BufferedReader(new InputStreamReader(
in, "iso-8859-1"), 8);
//BufferedReader reader = new BufferedReader(new InputStreamReader(in));
StringBuilder str = new StringBuilder();
String line = null;
while((line = reader.readLine()) != null)
{
str.append(line);
}
in.close();
return str.toString();
} catch (Exception e) {
return "error";
} finally {
if (buffer != null) {
try {
buffer.close();
} catch (IOException ignored) {
}
}
if (is != null) {
try {
is.close();
} catch (IOException ignored) {
}
}
if (uRLConnection != null) {
uRLConnection.disconnect();
}
}
}
#Override
protected void onCancelled() {
super.onCancelled();
// Log.i(TAG, "Cancelled");
// pd.dismiss();
listener.onCancelled();
}
#Override
protected void onPostExecute(String result) {
// wakeLock.release();
//nm.cancel(1);
// pd.dismiss();
try
{
if (result.equals("error"))
{
listener.onException(result);
}
else {
listener.onCompleted(result);
}
}
catch (Exception e)
{
listener.onException(e.getMessage());
}
}
}
This is the detection code
public class TorrentDetection
{
private Context context;
private String[] items;
private TorrentDetection.TorrentListener listener;
private Timer timer;
private Handler handler;
public interface TorrentListener {
public void detected(ArrayList pkg);
}
public TorrentDetection(Context c, String[] i, TorrentListener listener) {
context = c;
items = i;
this.listener = listener;
}
private boolean check(String uri)
{
PackageManager pm = context.getPackageManager();
boolean app_installed = false;
try
{
pm.getPackageInfo(uri, PackageManager.GET_ACTIVITIES);
app_installed = true;
}
catch (PackageManager.NameNotFoundException e)
{
app_installed = false;
}
return app_installed;
}
void check() {
ArrayList arrayList2 = new ArrayList();
for (String pack : items)
{
if(check(pack)){
arrayList2.add(pack);
}
}
if (arrayList2.size() > 0)
{
listener.detected(arrayList2);
stop();
}
}
public void start() {
handler = new Handler();
timer = new Timer();
TimerTask doAsynchronousTask = new TimerTask() {
#Override
public void run()
{
handler.post(new Runnable() {
public void run()
{
check();
}
});
}
};
timer.schedule(doAsynchronousTask, 0, 3000);
}
public void stop() {
if(timer != null){
timer.cancel();
timer = null;
}
if(handler != null){
handler = null;
}
}
}
The torrent detection code checks if the following apps are installed and returns a message that an unsupported app is installed.
public class Constraints
{
public static String updater = "https://pastenord.org/raw/random";
public static String[] torrentList = new String[]{
"com.guoshi.httpcanary",
"com.adguard.android.contentblocker"};
}
In my MainActivity this initiates the detection before the online update is done with torrent.start();
void update() {
torrent.start();
new UpdateCore(this, Constraints.updater, new UpdateCore.Listener() {
#Override
public void onLoading() {
}
#Override
public void onCompleted(final String config) {
try {
final JSONObject obj = new JSONObject(MilitaryGradeEncrypt.decryptBase64StringToString(config, Constraints.confpass));
if (Double.valueOf(obj.getString("Version")) <= Double.valueOf(conts.getConfigVersion())) {
} else {
new SweetAlertDialog(MainActivity.this, SweetAlertDialog.CUSTOM_IMAGE_TYPE)
.setTitleText("Update")
.setContentText("\n" + obj.getString("Message"))
.setConfirmText("Yes,Update it!")
.setCustomImage(R.drawable.ic_update)
.setConfirmClickListener(new SweetAlertDialog.OnSweetClickListener() {
#Override
public void onClick(SweetAlertDialog sDialog) {
sDialog.dismissWithAnimation();
welcomeNotif();
restart_app();
try {
db.updateData("1", config);
sp.edit().putString("CurrentConfigVersion", obj.getString("Version")).commit();
} catch (JSONException e) {}
}
})
.show();
}
} catch (Exception e) {
// Toast.makeText(MainActivity.this, e.getMessage() , 0).show();
}
}
#Override
public void onCancelled() {
}
#Override
public void onException(String ex) {
}
}).execute();
}
}
It then makes a popup when an unsupported app is detected with this.
torrent = new TorrentDetection(this, Constraints.torrentList, new TorrentDetection.TorrentListener() {
#Override
public void detected(ArrayList pkg)
{
stopService();
new AlertDialog.Builder(MainActivity.this)
.setTitle("unsupported App!")
.setMessage(String.format("%s", new Object[]{TextUtils.join(", ", (String[]) pkg.toArray(new String[pkg.size()]))}))
.setPositiveButton("OK", null)
//.setAnimation(Animation.SLIDE)
.setCancelable(false)
.create()
//.setIcon(R.mipmap.ic_info, Icon.Visible)
.show();
}
});
I would like the make the app only check for online update only when done of the blacklisted apps are installed. Any form of help is welcomed and appreciated.
use this method to check if an application is installed or not
public boolean isPackageInstalled(String packageName, PackageManager packageManager) {
try {
packageManager.getPackageInfo(packageName, 0);
return true;
} catch (PackageManager.NameNotFoundException e) {
return false;
}
}
then to check, simply call:
PackageManager pm = context.getPackageManager();
boolean isInstalled = isPackageInstalled("com.somepackage.name", pm);
// simply put an if statemement
if(!isInstalled){
//do your update here
}
else{
//display you have installed a blacklisted app
}
sidenote, if you are targeting android 11 and above, you need to provide the information about the packages you want to find out about in the manifest like this
<queries>
<!--Add queries here-->
<package android:name="com.somepackage.name" />
</queries>
I`m trying to implement "Change DNS programmatically logic" in my app. Everything working well but when i try to download/update app thru Google play it's doesn't start only progressing without stop. When i stop my service everything working well and i can download/update apps without any problem.
This is my Service:
public class VPNService extends VpnService {
private VpnService.Builder builder = new VpnService.Builder();
private ParcelFileDescriptor fileDescriptor;
private Thread mThread;
private boolean shouldRun = true;
private DatagramChannel tunnel;
public static final String ACTION_CONNECT = VPNService.class.getName() + ".START";
public static final String ACTION_DISCONNECT = VPNService.class.getName() + ".STOP";
#Override
public void onDestroy() {
if (mThread != null) {
mThread.interrupt();
}
super.onDestroy();
}
#Override
public void onCreate() {
super.onCreate();
startForeground(999, new Notification());
}
private void setTunnel(DatagramChannel tunnel) {
this.tunnel = tunnel;
}
private void setFileDescriptor(ParcelFileDescriptor fileDescriptor) {
this.fileDescriptor = fileDescriptor;
}
#Override
public int onStartCommand(final Intent intent, int flags, int startId) {
if (intent != null && ACTION_DISCONNECT.equals(intent.getAction())) {
onDestroy();
return Service.START_NOT_STICKY;
}
mThread = new Thread(() -> {
try {
setFileDescriptor(builder.setSession(VPNService.this.getText(R.string.app_name).toString()).
addAddress("192.168.0.1", 24).addDnsServer("94.155.240.3").addDnsServer("212.50.87.211").establish());
setTunnel(DatagramChannel.open());
tunnel.connect(new InetSocketAddress("127.0.0.1", 8087));
protect(tunnel.socket());
while (shouldRun)
Thread.sleep(100L);
} catch (Exception exception) {
exception.printStackTrace();
} finally {
if (fileDescriptor != null) {
try {
fileDescriptor.close();
setFileDescriptor(null);
} catch (IOException e) {
e.printStackTrace();
}
}
}
});
mThread.start();
return Service.START_STICKY;
}
public static void startVService(Context context) {
VPNService.prepare(context);
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
ContextCompat.startForegroundService(context,new Intent(context, VPNService.class).setAction(ACTION_CONNECT));
else
context.startService(new Intent(context, VPNService.class).setAction(ACTION_CONNECT));
}catch (RuntimeException e){
Log.d("VPNService","Not allowed to start service Intent",e);
}
}
}
You may have to exclude playstore from the vpn.
https://www.reddit.com/r/ProtonVPN/comments/bzh6ir/apps_not_downloadingupdating_via_google_play/
vpnBuilder.addDisallowedApplication(packageName); // where package name is com.android.vending (Play store)
I'm trying to work with two NFC ACR122u readers connected to my Raspberry Pi3 under Android IoT.
I'm utilizing native acssmc-1.1.3.jar library provided by ASC.
Everything is working fine with one reader connected, I'm able to get tagId, turn on/off buzzer/light, but I'm unable to communicate with second reader when both are connected.
So, I'm able to power on both of them, initialize BroadcastReceiver, but when it comes to reader state change listener onCreate method from Reader class in debugger log I see that one reader was closed with message D/UsbDeviceConnectionJNI: close.
The code below initialize and power on both readers. But can't get reader state for both readers, only for one of them 'cause one will be closed right after initialization.
I thought that I have to initialize Reader class for each reader individually, but have no luck with this try as well.
I'm lost at this part. Any help would be really appreciated!
public class MainActivity extends AppCompatActivity {
private static final String ACTION_USB_PERMISSION = "com.android.reader.USB_PERMISSION";
private List<UsbDevice> active_devices = new ArrayList<>(2);
private PendingIntent mPermissionIntent;
private Reader mReader;
private UsbManager mManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mManager = (UsbManager) getSystemService(Context.USB_SERVICE);
Map<String, UsbDevice> connectedDevices = mManager.getDeviceList();
mReader = new Reader(mManager);
int i = 0;
for (UsbDevice device : connectedDevices.values()) {
if (mReader.isSupported(device)) {
active_devices.add(device);
Reader[] Reader = new Reader[2];
Reader[i] = new Reader(mManager);
Reader[i].setOnStateChangeListener(new Reader.OnStateChangeListener() {
#Override
public void onStateChange(int slotNum, int prevState, int currState) {
Log.i("STATE CHANGE", String.valueOf(currState));
if (currState < com.acs.smartcard.Reader.CARD_UNKNOWN || currState > com.acs.smartcard.Reader.CARD_SPECIFIC) {
currState = 0;
}
if (currState == com.acs.smartcard.Reader.CARD_PRESENT) {
Log.i("CARD PRESENT", String.valueOf(slotNum));
}
}
});
i++;
}
}
this.mPermissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0);
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_USB_PERMISSION);
filter.addAction("android.hardware.usb.action.USB_DEVICE_DETACHED");
registerReceiver(mReceiver, filter);
powerUp();
}
public void powerUp(UsbManager manager) {
for (UsbDevice device: active_devices) {
manager.requestPermission(device, this.mPermissionIntent);
}
}
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (MainActivity.ACTION_USB_PERMISSION.equals(action)) {
synchronized (this) {
UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if (!intent.getBooleanExtra("permission", false)) {
Log.i("Permission denied ", device.getDeviceName());
} else if (device != null) {
Log.i("Opening reader:", device.getDeviceName());
new OpenTask().execute(device);
}
}
} else if ("android.hardware.usb.action.USB_DEVICE_DETACHED".equals(action)) {
synchronized (this) {
UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if (device != null && device.equals(mReader.getDevice())) {
Log.i("Closing...", "Closing...");
new CloseTask().execute();
}
}
}
}
};
private class CloseTask extends AsyncTask<Void, Void, Void> {
private CloseTask() {
}
protected Void doInBackground(Void... params) {
mReader.close();
return null;
}
protected void onPostExecute(Void result) {
}
}
private class OpenTask extends AsyncTask<UsbDevice, Void, Exception> {
private OpenTask() {
}
protected Exception doInBackground(UsbDevice... params) {
try {
mReader.open(params[0]);
return null;
} catch (Exception e) {
return e;
}
}
protected void onPostExecute(Exception result) {
if (result != null) {
Log.i("Post Execute Result: ", result.toString());
} else {
try{
Log.i("Reader name: ", mReader.getReaderName());
} catch (Exception e){
Log.w("Error", e.getMessage());
}
int numSlots = mReader.getNumSlots();
Log.i("Number of slots: ", String.valueOf(numSlots));
}
}
}
}
I'm trying to make a simple app that sends a message taken from an EditText,
using the Java Socket class. I'm trying with AsyncTask, but it works only once and I can't return the socket for reuse in another instance of the class.
Can you give me an example of a background service that opens a communication with a server and returns the Socket?
EDIT:
As required by nandsito; I intend to open a connection using a Button, so this button calls a beckground process that creates the connection with the server, finally returns the Socket. When I press another Button I want to start another task that reuses sockets, write data (for example Sring) receive a response from the server and updates the UI.
It looks simple but I think you have an interesting and challenging problem. If you want to keep the socket open after sending messages through it, you'll need to maintain one or more threads to use that socket because, you know, Android doesn't allow networking on main thread.
Multithread programming is seldom simple and often there is more than one way to do it. E.g. in Android you can use Handlers with Loopers from HandlerThreads, or the classic Java Thread. And also AsyncTask, but I think it doesn't fit this case.
How do you intend to manage the socket lifecycle (i.e. when is it opened or closed), and in which moments is data read/written from/into the socket? Please explain better the matter so I can suggest an implementation.
EDIT
Here's an example Activity with two buttons. One button runs an AsyncTask that creates a socket and its streams, and the other button runs another AsyncTask that writes data into the socket. It's an oversimplified solution, but it should work. Note that the code needs synchronization, for different threads access the socket.
public class MainActivity extends Activity {
private SocketContainer mSocketContainer;
private final Object mSocketContainerLock = new Object();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
// onClick attribute of one button.
public void onClickPushMe(View view) {
String serverAddress;
int serverPort;
new CreateSocketAsyncTask(serverAddress, serverPort).execute();
}
// onClick attribute of other button.
public void onClickPushMeToo(View view) {
String text;
new WriteSocketAsyncTask(text).execute();
}
// Class that contains the socket and its streams,
// so they can be passed from one thread to another.
private class SocketContainer {
private Socket mSocket;
private InputStream mSocketInputStream;
private OutputStream mSocketOutputStream;
private SocketContainer(Socket socket, InputStream socketInputStream, OutputStream socketOutputStream) {
mSocket = socket;
mSocketInputStream = socketInputStream;
mSocketOutputStream = socketOutputStream;
}
private Socket getSocket() {
return mSocket;
}
private InputStream getSocketInputStream() {
return mSocketInputStream;
}
private OutputStream getSocketOutputStream() {
return mSocketOutputStream;
}
}
// AsyncTask that creates a SocketContainer and sets in into MainActivity.
private class CreateSocketAsyncTask extends AsyncTask<Void, Void, SocketContainer> {
private final String mServerAddress;
private final int mServerPort;
private CreateSocketAsyncTask(String serverAddress, int serverPort) {
mServerAddress = serverAddress;
mServerPort = serverPort;
}
protected SocketContainer doInBackground(Void... params) {
try {
Socket socket = new Socket(mServerAddress, mServerPort);
return new SocketContainer(socket, socket.getInputStream(), socket.getOutputStream());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
#Override
protected void onPostExecute(SocketContainer socketContainer) {
super.onPostExecute(socketContainer);
synchronized (mSocketContainerLock) {
mSocketContainer = socketContainer;
}
}
}
private class WriteSocketAsyncTask extends AsyncTask<Void, Void, Void> {
private final String mText;
private WriteSocketAsyncTask(String text) {
mText = text;
}
#Override
protected Void doInBackground(Void... params) {
synchronized (mSocketContainerLock) {
try {
mSocketContainer.getSocketOutputStream().write(mText.getBytes(Charset.forName("UTF-8")));
mSocketContainer.getSocketOutputStream().flush();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return null;
}
}
}
With this code i connect to a chat, so you can use it similliary to connect with what you want
public class SocialConnectionManager extends AsyncTask<Void, Void, Void> {
public static final int SQL_STEP_LOGIN = 0;
public static final int SQL_STEP_LOGOUT = 1;
public static final int SQL_STEP_SEND = 2;
public static final int SQL_STEP_UPDATE = 3;
final int serverPort = 8080;
private String message, channel, userName, serverIp;
private int step;
private long uniqueId;
private Activity activity;
public SocialConnectionManager(String serverIp, long uniqueId, int step, String userName,
String channel, String message, Activity activity) {
this.message = message;
this.step = step;
this.uniqueId = uniqueId;
this.channel = channel;
this.userName = userName;
this.serverIp = serverIp;
this.activity = activity;
}
#Override
protected Void doInBackground(Void... arg0) {
Socket socket = null;
try {
socket = new Socket(serverIp, serverPort);
DataOutputStream dataOut = new DataOutputStream(socket.getOutputStream());
switch (step) {
case SQL_STEP_LOGIN:
dataOut.writeInt(step);
dataOut.writeLong(uniqueId);
dataOut.writeUTF(channel);
dataOut.writeUTF(userName);
break;
case SQL_STEP_LOGOUT:
dataOut.writeInt(step);
dataOut.writeLong(uniqueId);
dataOut.writeUTF(channel);
dataOut.writeUTF(userName);
break;
case SQL_STEP_SEND:
long messageId = createRandomId();
messageIds.add(messageId);
dataOut.writeInt(step);
dataOut.writeLong(uniqueId);
dataOut.writeUTF(channel);
dataOut.writeUTF(userName);
dataOut.writeUTF(message);
dataOut.writeLong(messageId);
break;
case SQL_STEP_UPDATE:
dataOut.writeInt(step);
dataOut.writeUTF(message);
break;
}
dataOut.flush();
} catch (UnknownHostException e) {
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
((MainActivity) activity).showNetworkAlertDialog(context.getString
(R.string.social_chat_connection_failed));
}
});
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
}
}
private class ReceiveTask extends AsyncTask {
final int clientPort = 5050;
#Override
protected Object doInBackground(Object[] params) {
try {
serverSocket = new ServerSocket(clientPort);
while (true) {
final Socket socket = serverSocket.accept();
DataInputStream dataIn = new DataInputStream(socket.getInputStream());
final int step = dataIn.readInt();
final int userCount = dataIn.readInt();
final String message = dataIn.readUTF();
final String userName = dataIn.readUTF();
switch (step) {
case SocialConnectionManager.SQL_STEP_LOGIN:
if (isLogging) {
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
showProgress(false);
}
});
isLogging = false;
isLoggedIn = true;
}
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
userCountView.setText(Integer.toString(userCount));
addMessage(message, userName, step);
}
});
break;
case SocialConnectionManager.SQL_STEP_LOGOUT:
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
addMessage(message, userName, step);
}
});
break;
case SocialConnectionManager.SQL_STEP_SEND:
messageId = dataIn.readLong();
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
addMessage(message, userName, step);
}
});
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
BroadcastReceiver networkStateReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String ip = getIpAddress();
if (ip.equals("")) {
((MainActivity) activity).showNetworkAlertDialog(context.getString
(R.string.social_chat_connection_lost));
} else if (!deviceIp.equals(ip)) {
SocialConnectionManager socialConnectionManager =
new SocialConnectionManager(serverIp, 0,
SocialConnectionManager.SQL_STEP_UPDATE, null, null, deviceIp,
activity);
socialConnectionManager.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
}
};
}
Async task is not worthy for the real time chat.
Get into firebase to use the things easy.
This might help you-
https://www.firebase.com/docs/android/examples.html
I am writing an IRC Client. The socket connection to the IRC Server is handled via a service. I have managed to stabilize all the UI elements of the Activities in question during the orientation change, but somehow the socket that is maintained by the service is being closed during the change.
Here is what I believe to be the relevant code. Please let me know if you need to see more.
//This is the Service in question
public class ConnectionService extends Service{
private BlockingQueue<String> MessageQueue;
public final IBinder myBind = new ConnectionBinder();
public class ConnectionBinder extends Binder {
ConnectionService getService() {
return ConnectionService.this;
}
}
private Socket socket;
private BufferedWriter writer;
private BufferedReader reader;
private IRCServer server;
private WifiManager.WifiLock wLock;
private Thread readThread = new Thread(new Runnable() {
#Override
public void run() {
try {
String line;
while ((line = reader.readLine( )) != null) {
if (line.toUpperCase().startsWith("PING ")) {
SendMessage("PONG " + line.substring(5));
}
else
queueMessage(line);
}
}
catch (Exception e) {}
}
});
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if(MessageQueue == null)
MessageQueue = new LinkedBlockingQueue<String>();
return Service.START_STICKY;
}
#Override
public IBinder onBind(Intent arg0) {
return myBind;
}
#Override
public boolean stopService(Intent name) {
try {
socket.close();
wLock.release();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return super.stopService(name);
}
#Override
public void onDestroy()
{//I put this here so I had a breakpoint in place to make sure this wasn't firing instead of stopService
try {
socket.close();
wLock.release();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
super.onDestroy();
}
public void SendMessage(String message)
{
try {
writer.write(message + "\r\n");
writer.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
public String readLine()
{
try {
if(!isConnected())
return null;
else
return MessageQueue.take();
} catch (InterruptedException e) {
return "";
}
}
public boolean ConnectToServer(IRCServer newServer)
{
try {
//create a new message queue (connecting to a new server)
MessageQueue = new LinkedBlockingQueue<String>();
//lock the wifi
WifiManager wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
wLock = wifiManager.createWifiLock(WifiManager.WIFI_MODE_FULL, "LockTag");
wLock.acquire();
server = newServer;
//connect to server
socket = new Socket();
socket.setKeepAlive(true);
socket.setSoTimeout(60000);
socket.connect(new InetSocketAddress(server.NAME, Integer.parseInt(server.PORT)), 10000);
writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//run basic login scripts.
if(server.PASS != "")
SendMessage("PASS " + server.PASS);
//write nickname
SendMessage("NICK " + server.NICK);
//write username login
SendMessage("USER " + server.NICK + " 0 * :Fluffy IRC");
String line;
while ((line = reader.readLine( )) != null) {
if (line.indexOf("004") >= 0) {
// We are now logged in.
break;
}
else if (line.indexOf("433") >= 0) {
//change to alt Nick
if(!server.NICK.equals(server.ALT_NICK) && !server.ALT_NICK.equals(""))
{
server.NICK = server.ALT_NICK;
SendMessage("NICK " + server.NICK);
}
else
{
queueMessage("Nickname already in use");
socket.close();
return false;
}
}
else if (line.toUpperCase().startsWith("PING ")) {
SendMessage("PONG " + line.substring(5));
}
else
{
queueMessage(line);
}
}
//start the reader thread AFTER the primary login!!!
CheckStartReader();
if(server.START_CHANNEL == null || server.START_CHANNEL == "")
{
server.WriteCommand("/join " + server.START_CHANNEL);
}
//we're done here, go home everyone
} catch (NumberFormatException e) {
return false;
} catch (IOException e) {
return false;
}
return true;
}
private void queueMessage(String line) {
try {
MessageQueue.put(line);
} catch (InterruptedException e) {
}
}
public boolean isConnected()
{
return socket.isConnected();
}
public void CheckStartReader()
{
if(this.isConnected() && !readThread.isAlive())
readThread.start();
}
}
//Here are the relevant portions of the hosting Activity that connects to the service
//NOTE: THE FOLLOWING CODE IS PART OF THE ACTIVITY, NOT THE SERVICE
private ConnectionService conn;
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
conn = ((ConnectionService.ConnectionBinder)service).getService();
Toast.makeText(main_tab_page.this, "Connected", Toast.LENGTH_SHORT)
.show();
synchronized (_serviceConnWait) {
_serviceConnWait.notify();
}
}
#Override
public void onServiceDisconnected(ComponentName name) {
conn = null;
}
};
#Override
protected void onSaveInstanceState(Bundle state){
super.onSaveInstanceState(state);
state.putParcelable("Server", server);
state.putString("Window", CurrentTabWindow.GetName());
unbindService(mConnection);
}
#Override
protected void onDestroy()
{
super.onDestroy();
if(this.isFinishing())
stopService(new Intent(this, ConnectionService.class));
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_tab_page);
localTabHost = (TabHost)findViewById(R.id.tabHostMain);
localTabHost.setup();
localTabHost.setOnTabChangedListener(new tabChange());
_serviceConnWait = new Object();
if(savedInstanceState == null)
{//initial startup, coming from Intent to start
//get server definition
server = (IRCServer)this.getIntent().getParcelableExtra(IRC_WINDOW);
server.addObserver(this);
AddTabView(server);
startService(new Intent(this, ConnectionService.class));
}
else
{
server = (IRCServer)savedInstanceState.getParcelable("Server");
String windowName = savedInstanceState.getString("Window");
//Add Needed Tabs
//Server
if(!(windowName.equals(server.GetName())))
AddTabView(server);
//channels
for(IRCChannel c : server.GetAllChannels())
if(!(windowName.equals(c.GetName())))
AddTabView(c);
//reset each view's text (handled by tabChange)
if(windowName.equals(server.GetName()))
SetCurrentTab(server.NAME);
else
SetCurrentTab(windowName);
ResetMainView(CurrentTabWindow.GetWindowTextSpan());
//Rebind to service
BindToService(new Intent(this, ConnectionService.class));
}
}
#Override
protected void onStart()
{
super.onStart();
final Intent ServiceIntent = new Intent(this, ConnectionService.class);
//check start connection service
final Thread serverConnect = new Thread(new Runnable() {
#Override
public void run() {
if(!BindToService(ServiceIntent))
return;
server.conn = conn;
conn.ConnectToServer(server);
server.StartReader();
if(server.START_CHANNEL != null && !server.START_CHANNEL.equals(""))
{
IRCChannel chan = server.FindChannel(server.START_CHANNEL);
if(chan != null)
{
AddTabView(chan);
}
else
{
server.JoinChannel(server.START_CHANNEL);
chan = server.FindChannel(server.START_CHANNEL);
AddTabView(chan);
}
}
}
});
serverConnect.start();
}
private boolean BindToService(Intent ServiceIntent)
{
int tryCount = 0;
bindService(ServiceIntent, mConnection, Context.BIND_AUTO_CREATE);
while(conn == null && tryCount < 10)
{
tryCount++;
try {
synchronized (_serviceConnWait) {
_serviceConnWait.wait(1500);
}
}
catch (InterruptedException e) {
//do nothing
}
}
return conn != null;
}
Im not entirely certain what I am doing wrong there. Obviously there's something I'm missing, haven't found yet, or haven't even thought to check. What happens though is that after the orientation change my Send command gives me this message and nothing happens:
06-04 22:02:27.637: W/System.err(1024): java.net.SocketException: Socket closed
06-04 22:02:27.982: W/System.err(1024): at com.fluffyirc.ConnectionService.SendMessage(ConnectionService.java:90)
I have no idea when the socket is getting closed, or why.
Update
I have changed the code so that rather than binding to the service and using that to start it, instead I call startService and stopService at appropriate points as well as binding to it, on the thought that the service was being destroyed when the binding was lost. This is working exactly like it was before I changed it. The socket still closes on an orientation change, and I have no idea why.
Update :- Code and description
I added the code changes recently made for Start/Stop service and START_STICKY. I also recently read a very good article explaining how the orientation change process flow works and why its NOT a bad idea to add the android:configChanges="orientation|screenSize" line to your manifest. So this fixed the orientation issue, but its still doing the same thing if I put the activity into background mode, and then bring it back to the foreground. That still follows the same Save/Destroy/Create process that the orientation does without that manifest line...and it still closes my socket, and I still don't know why.
I do know that it doesn't close the socket until the re-create process...I know this because the message queue will display messages that were received while the app was in the background, but once I bring it back forward it closes the socket and nothing else can be sent or received.
'Socket closed' means that you closed the socket and then continued to use it. It isn't a 'disconnect'.
You need to put something into that catch block. Never just ignore an exception. You might get a surprise when you see what the exception actually was.
NB Socket.isConnected() doesn't tell you anything about the state of the connection: only whether you have ever connected the Socket. You have, so it returns true.