Moving to next activity once my QR Code has been scanned - java

I have limited Android Experience and had a very basic doubt. My scenario is as follows:
I currently have 2 applications, one being a QR Code Scanner and another which displays a QR Code. These will be running on multiple devices. The communication steps which take place are as follows:
Prior Setup:
There is a firebase database containing strings for QR Codes to be generated.
Device 2 reads the Code off the Firebase Database and displays it on the Screen (2nd App).
Device Communication:
Device 1 has the Scanner App and Device 2 has the QR Code displayed on Screen.
Device 1 now scans the QR Code from the Device 2 and verifies through some logic whether QR Code is valid or not.
If QR Code is valid, then the following takes place:
Device 1 calculates a new QR Code and places it on the Firebase Database.
The Device 2 should now move from displaying the QR Code to another activity which has the logic to Scan QR Codes of other Devices and verifies if they are correct.
Device 3 onwards must display a new QR Code which is on the Firebase Database which can now be scanned by Devices 1 and 2.
Note: The QR Code Updates on UI must keep happening until there is some sort of indication which makes the Device move to the QR Code Scanning stage.
Things which are working:
The 2 activities of the application (QR Code Display and QR Code Scanning) working independently.
QR Code Updates on UI whenever Firebase Database updated.
Things which are not working:
Moving from QR Code Display to Scanning once the QR Code is deemed valid.
Things I have tried:
Creating a Server Socket Implementation on the QR Code Display Application which is running as a Service called by my Main Activity. Client Socket Implementation (placed as a Service) is on the QR Code Scanner, which will send data to the Listening Server Socket once the QR Code is deemed valid. (Issue is that neither data is sent nor received).
Creating a Server Socket Implementation on the QR Code Display Application which is running as a Service called by my Main Activity. Client Socket Implementation (placed on UI Thread) is on the QR Code Scanner, which will send data to the Listening Server Socket once the QR Code is deemed valid.(Issue is that neither data is sent nor received)
I am very confused as to whether my approach is correct. Is there a better way to do it? Code for my service is as follows:
Device 2 - QR Code Display App:
Service
public class MyService extends Service {
private static final String TAG = "MyService";
private T_Client client;
#Override
public void onDestroy() {
Log.v(TAG, "onDestroy");
if (client != null) {
try {
client.stopClient();
} catch (Exception e) {
Log.e(TAG, "Error on close: " + e);
}
}
super.onDestroy();
Toast.makeText(this, "Service stopped", Toast.LENGTH_LONG).show();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.v(TAG, "onStartCommand");
client = new T_Client();
client.start();
Toast.makeText(this, "Service started", Toast.LENGTH_LONG).show();
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
}
Display Server Implementation (Called T_Client)
public class T_Client extends Thread {
private static final String TAG = "T_Client";
private Socket sock = null;
private boolean running = false;
private ObjectInputStream in;
private ObjectOutputStream out;
private Object objIn;
public void send(String _msg) {
if (out != null) {
try {
out.writeObject(_msg);
out.flush();
Log.i("Send Method", "Outgoing : " + _msg.toString());
} catch (IOException ex) {
Log.e("Send Method", ex.toString());
}
}
}
public void stopClient() {
Log.v(TAG,"stopClient method run");
running = false;
}
#Override
public void run() {
running = true;
try {
ServerSocket sock1 = new ServerSocket(9999);
try {
Log.i(TAG, "C: Connected.");
while (running) {
sock = sock1.accept();
out = new ObjectOutputStream(sock.getOutputStream());
in = new ObjectInputStream(sock.getInputStream());
objIn = in.readObject();
Log.i("Object Read Class", objIn.getClass().toString());
Log.i("Object Read", objIn.toString());
/* Currently commented because startActivity not recognised
if (objIn != null) {
Intent dialogIntent = new Intent();
dialogIntent.setClass(this, MainActivity.class);
dialogIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(dialogIntent);
}
Atleast the data should get read here
*/
System.out.println("Object Read Class" + objIn.getClass().toString());
System.out.println("Object Read" + objIn.toString());
}
Log.e("RESPONSE FROM SERVER", "S: Received Message: '" + objIn + "'");
} catch (Exception e) {
Log.e(TAG, "S: Error", e);
} finally {
out.close();
in.close();
sock.close();
Log.i(TAG, "Closing socket: " + sock);
}
} catch (Exception e) {
Log.e(TAG, "C: Error", e);
}
}
}
Intent intent=new Intent(getContext().getApplicationContext(),MyService.class);
getContext().startService(intent);
Scanner Application: (Written in Kotlin)
Scanner Client Implementation (Called T_Server)
internal class T_Server : Thread() {
private var sock: Socket? = null
private var running = false
private var out: ObjectOutputStream? = null
private val objIn: Any? = null
var blockchain_kotlin_copy = SecondActivity().blockchain_kotlin_copy
fun send(_msg: String) {
if (out != null) {
try {
out!!.writeObject(_msg)
out!!.flush()
Log.i("Send Method", "Outgoing : $_msg")
} catch (ex: IOException) {
Log.e("Send Method", ex.toString())
}
}
}
fun stopClient() {
Log.v(TAG, "stopClient method run")
running = false
}
override fun run() {
running = true
try {
val sock1 = ServerSocket(9999)
try {
Log.i(TAG, "C: Connected.")
while (running) {
sock = sock1.accept()
try {
out = ObjectOutputStream(sock!!.getOutputStream())
out!!.writeObject(blockchain_kotlin_copy)
out!!.flush()
out!!.reset()
Log.i("Send Method", "Outgoing : $blockchain_kotlin_copy")
println("Out is being sent")
println("$blockchain_kotlin_copy")
} catch (ex: IOException) {
Log.e("Send Method", ex.toString())
}
}
} catch (e: Exception) {
Log.e(TAG, "S: Error", e)
} finally {
out!!.close()
sock!!.close()
Log.i(TAG, "Closing socket: " + sock!!)
}
} catch (e: Exception) {
Log.e(TAG, "C: Error", e)
}
}
companion object {
private val TAG = "T_Server"
}
}
Service
public class MyService extends Service {
private static final String TAG = "MyService";
private T_Server client;
#Override
public void onDestroy() {
Log.v(TAG, "onDestroy");
if (client != null) {
try {
client.stopClient();
} catch (Exception e) {
Log.e(TAG, "Error on close: " + e);
}
}
super.onDestroy();
Toast.makeText(this, "Service stopped", Toast.LENGTH_LONG).show();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.v(TAG, "onStartCommand");
client = new T_Server();
client.start();
Toast.makeText(this, "Service started", Toast.LENGTH_LONG).show();
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
}
Main Activity
val intent = Intent(this, MyService::class.java)
this.startService(intent)

It sounds like you might be interested in Firebase Device-to-Device notification. You might do as follows
Device X displays QR code
Some Device Y reads the QR code from Device X and if(verified) sendNotificationToDevice(X)
Device X moves to Scanner activity to read from some other Device Z.
Besides the link above, there are a number of YouTube tutorials and Medium blog posts on how to implement Device-to-Device notification.

Related

BLE on tinyB java characteristic has no notify

i`m trying to connect simple device(AB Shutter3) to java application on PC, but get no notification till connect. For test try
BluetoothDevice currentDev = (BluetoothDevice) container.getDevice();
currentDev.setTrusted(true);
if(!currentDev.getConnected()) {
currentDev.connect();
}
logger.info(currentDev.getName());
then get ALL services, descriptors and try to enable notifications like:
public void findServiceAndCharacteristic(BlueToothDeviceContainer container, String serviceUid){
for (BluetoothGattService service : container.getGattServices()) {
if(service.getUUID().equals(CUSTOM_SHUTTER_SERVICE_2.value) ||
service.getUUID().equals(CUSTOM_SHUTTER_SERVICE.value) ||
service.getUUID().equals(GENERAL_DEVICE_INFORMATION.value)){
List<BluetoothGattCharacteristic> characteristics = service.getCharacteristics();
for(BluetoothGattCharacteristic ch : characteristics){
logger.info(ch.getUUID());
try {
List<BluetoothGattDescriptor> descrs = ch.getDescriptors();
for(BluetoothGattDescriptor ds : descrs){
ds.enableValueNotifications(new BluetoothNotification<byte[]>() {
#Override
public void run(byte[] bytes) {
logger.info(ds.getUUID() + "getted");
logger.info("BUTTON WAS PUSHED!!!");
}
});
}
} catch (BluetoothException ex){
logger.warn(ch.getUUID() + " does not work.");
}
try{
BluetoothNotification btnm = new BluetoothNotification<byte[]>() {
#Override
public void run(byte[] bytes) {
logger.info(ch.getUUID() + "getted CH");
logger.info("BUTTON WAS PUSHED!!!");
}
};
ch.enableValueNotifications(btnm);
} catch (BluetoothException ex){
logger.warn(ch.getUUID() + " does not work.");
}
}
but run method does not work. What am i doing wrong, does anybody knows?
Problem was not at library. Problem at HID characteristic. It was not found on device, so there was no notifications from it.

Getting socket data on seperate thread and then passing it to main thread

Edited my question for clarification and code:
My goal is to pass my String data from my background thread, to my main application thread. Any help is appreciated.
Here is the code that creates the main background thread. This is located in my Server.java class
public class Server {
boolean isConnected = false;
Controller controller = new Controller();
public void startHost() {
Thread host = new Thread(() -> {
Controller controller = new Controller();
ServerSocket server = null;
try {
server = new ServerSocket(GeneralConstants.applicationPort);
} catch (BindException e2) {
System.out.println("Port Already in Use!");
} catch (IOException e) {
//do nothing
}
while (true) {
if (server == null) { break; }
try {
Socket client = server.accept();
System.out.println("Client Connected: " + isConnected);
if (!isConnected) {
controller.createClientHandler(client);
isConnected = true;
System.out.println("Client Connected: " + isConnected);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
host.setDaemon(true);
host.start();
}
Here is the code that is then called when a client is connected, located in my Controller.java class.
public synchronized void createClientHandler(Socket client) {
boolean alreadyConnected = false;
if (alreadyConnected) {
//do NOT assign multiple threads for each client
} else {
ClientHandler handleClients = new ClientHandler("client", client);
}
}
The program then creates two background threads for my client, one to manage receiving messages, and sending messages.
public ClientHandler(String name, Socket s) {
clientSocket = s;
clientName = name;
receiveThread = new Thread(this::receive);
sendThread = new Thread(this::send);
connected = clientSocket.isConnected();
receiveThread.start();
sendThread.start();
}
The thread then successfully creates the inputstream and passes the object to my controller. Which then process and grabs a string assigning it to a variable
public synchronized void handleReceivedPacket(String name, BufferedReader in) {
try {
data = in.readLine();
System.out.println("Successfully assigned data to: " + data);
} catch (IOException e) {
System.out.println("Unable to read result data");
}
}
How do I access my String data from the main thread without getting null?
Aka I can call (or something similar)
controller.returnData();
from my main application. From which it'll either return null (no data yet), or actually return my data. Right now, it's always null.
Edit, this is what's actually calling controller.returnData() {
I don't want to paste a massive amount of code for fear of reaching StackOverflow's code limit, so here's my application structure.
My JavaFX creates the scene, and creates a root gridpane, it then calls a method that creates sub gridpanes based the specified input. Aka, a user can press "Main Menu" that calls my method setScene() which removes the current "sub-root" gridpane and creates a "new" scene. Right now, I have a GameBoard.java class which on button press, calls controller.returnData()
PassOption.setOnAction(event -> {
System.out.println(controller.returnData());
});
There is no functional purpose for this besides testing. If I can receive the data, then I can expand on this using the data.
Start thinking about design. In network applications you typically have to manage the following responsibilites:
Connected clients and their state (connection state, heartbeats, ...)
Received messages from the clients
Messages to transmit to the clients
It makes sense to separate those responsibilities in order to keep the code clean, readable and maintainable.
Separation can mean both, thread-wise and class-wise.
For example, you could implement it as follows:
The class ClientAcceptor is responsible for opening the socket and accepting clients. As soon as a client has connected, it delegates the further work to a controller and then waits for other clients:
public class ClientAcceptor implements Runnable {
#Override
public void run() {
while (true) {
ServerSocket server;
try {
server = new ServerSocket(1992);
Socket client = server.accept();
if (client.isConnected()) {
controller.createClientHandler(client);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
The controller could then create a handler (if the controller decides to do so, e.g. it could also decline the client). The ClientHandler class could look as follows:
public class ClientHandler {
private Thread receiveThread;
private Thread sendThread;
private boolean connected;
private Socket clientSocket;
private String clientName;
private LinkedBlockingDeque<byte[]> sendQueue;
public ClientHandler(String name, Socket s) {
clientSocket = s;
clientName = name;
receiveThread = new Thread(() -> receive());
sendThread = new Thread(() -> send());
connected = clientSocket.isConnected();
receiveThread.start();
sendThread.start();
}
private void receive() {
BufferedInputStream in = null;
try {
in = new BufferedInputStream(clientSocket.getInputStream());
} catch (IOException e) {
connected = false;
}
while (connected) {
try {
byte[] bytes = in.readAllBytes();
if (bytes != null && bytes.length > 0) {
controller.handleReceivedPacket(clientName, bytes);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void send() {
BufferedOutputStream out = null;
try {
out = new BufferedOutputStream(clientSocket.getOutputStream());
} catch (IOException e) {
connected = false;
}
while (connected) {
byte[] toSend = sendQueue.getFirst();
if (toSend != null && toSend.length > 0) {
try {
out.write(toSend);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public void send(byte[] packet) {
sendQueue.add(packet);
}
public void close() {
connected = false;
}
}
The ClientHandler is responsible for receiving and transmitting data. If a packet arrives it informes the controller, which parses the packet. The ClientHandler also provides a public API to send data (which is stored in a queue and handled by a thread) and close the connection.
The above code examples are neither tested, nor complete. Take it as a starting point.

Bluetooth Connection to Raspberry Pi 3: java.io.IOException: read failed, socket might closed or timeout, read ret: -1 [duplicate]

Currently I am trying to deal with a strange Exception when opening a BluetoothSocket on my Nexus 7 (2012), with Android 4.3 (Build JWR66Y, I guess the second 4.3 update). I have seen some related postings (e.g. https://stackoverflow.com/questions/13648373/bluetoothsocket-connect-throwing-exception-read-failed), but none seems to provide a workaround for this issue. Also, as suggested in these threads, re-pairing does not help, and constantly trying to connect (through a stupid loop) also has no effect.
I am dealing with an embedded device (a noname OBD-II car adapter, similar to http://images04.olx.com/ui/15/53/76/1316534072_254254776_2-OBD-II-BLUTOOTH-ADAPTERSCLEAR-CHECK-ENGINE-LIGHTS-WITH-YOUR-PHONE-Oceanside.jpg). My Android 2.3.7 phone does not have any issues connecting, and the Xperia of a colleague (Android 4.1.2) also works. Another Google Nexus (I dont know if 'One' or 'S', but not '4') also fails with Android 4.3.
Here is the Snippet of the connection establishment. It is running in its own Thread, created within a Service.
private class ConnectThread extends Thread {
private static final UUID EMBEDDED_BOARD_SPP = UUID
.fromString("00001101-0000-1000-8000-00805F9B34FB");
private BluetoothAdapter adapter;
private boolean secure;
private BluetoothDevice device;
private List<UUID> uuidCandidates;
private int candidate;
protected boolean started;
public ConnectThread(BluetoothDevice device, boolean secure) {
logger.info("initiliasing connection to device "+device.getName() +" / "+ device.getAddress());
adapter = BluetoothAdapter.getDefaultAdapter();
this.secure = secure;
this.device = device;
setName("BluetoothConnectThread");
if (!startQueryingForUUIDs()) {
this.uuidCandidates = Collections.singletonList(EMBEDDED_BOARD_SPP);
this.start();
} else{
logger.info("Using UUID discovery mechanism.");
}
/*
* it will start upon the broadcast receive otherwise
*/
}
private boolean startQueryingForUUIDs() {
Class<?> cl = BluetoothDevice.class;
Class<?>[] par = {};
Method fetchUuidsWithSdpMethod;
try {
fetchUuidsWithSdpMethod = cl.getMethod("fetchUuidsWithSdp", par);
} catch (NoSuchMethodException e) {
logger.warn(e.getMessage());
return false;
}
Object[] args = {};
try {
BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
BluetoothDevice deviceExtra = intent.getParcelableExtra("android.bluetooth.device.extra.DEVICE");
Parcelable[] uuidExtra = intent.getParcelableArrayExtra("android.bluetooth.device.extra.UUID");
uuidCandidates = new ArrayList<UUID>();
for (Parcelable uuid : uuidExtra) {
uuidCandidates.add(UUID.fromString(uuid.toString()));
}
synchronized (ConnectThread.this) {
if (!ConnectThread.this.started) {
ConnectThread.this.start();
ConnectThread.this.started = true;
unregisterReceiver(this);
}
}
}
};
registerReceiver(receiver, new IntentFilter("android.bleutooth.device.action.UUID"));
registerReceiver(receiver, new IntentFilter("android.bluetooth.device.action.UUID"));
fetchUuidsWithSdpMethod.invoke(device, args);
} catch (IllegalArgumentException e) {
logger.warn(e.getMessage());
return false;
} catch (IllegalAccessException e) {
logger.warn(e.getMessage());
return false;
} catch (InvocationTargetException e) {
logger.warn(e.getMessage());
return false;
}
return true;
}
public void run() {
boolean success = false;
while (selectSocket()) {
if (bluetoothSocket == null) {
logger.warn("Socket is null! Cancelling!");
deviceDisconnected();
openTroubleshootingActivity(TroubleshootingActivity.BLUETOOTH_EXCEPTION);
}
// Always cancel discovery because it will slow down a connection
adapter.cancelDiscovery();
// Make a connection to the BluetoothSocket
try {
// This is a blocking call and will only return on a
// successful connection or an exception
bluetoothSocket.connect();
success = true;
break;
} catch (IOException e) {
// Close the socket
try {
shutdownSocket();
} catch (IOException e2) {
logger.warn(e2.getMessage(), e2);
}
}
}
if (success) {
deviceConnected();
} else {
deviceDisconnected();
openTroubleshootingActivity(TroubleshootingActivity.BLUETOOTH_EXCEPTION);
}
}
private boolean selectSocket() {
if (candidate >= uuidCandidates.size()) {
return false;
}
BluetoothSocket tmp;
UUID uuid = uuidCandidates.get(candidate++);
logger.info("Attempting to connect to SDP "+ uuid);
try {
if (secure) {
tmp = device.createRfcommSocketToServiceRecord(
uuid);
} else {
tmp = device.createInsecureRfcommSocketToServiceRecord(
uuid);
}
bluetoothSocket = tmp;
return true;
} catch (IOException e) {
logger.warn(e.getMessage() ,e);
}
return false;
}
}
The code is failing at bluetoothSocket.connect(). I am getting a java.io.IOException: read failed, socket might closed, read ret: -1. This is the corresponding source at GitHub: https://github.com/android/platform_frameworks_base/blob/android-4.3_r2/core/java/android/bluetooth/BluetoothSocket.java#L504
Its called through readInt(), called from https://github.com/android/platform_frameworks_base/blob/android-4.3_r2/core/java/android/bluetooth/BluetoothSocket.java#L319
Some metadata dump of the used socket resulted in the following information. These are exactly the same on Nexus 7 and my 2.3.7 phone.
Bluetooth Device 'OBDII'
Address: 11:22:33:DD:EE:FF
Bond state: 12 (bonded)
Type: 1
Class major version: 7936
Class minor version: 7936
Class Contents: 0
Contents: 0
I have some other OBD-II adapters (more expansives) and they all work. Is there any chance, that I am missing something or might this be a bug in Android?
I have finally found a workaround. The magic is hidden under the hood of the BluetoothDevice class (see https://github.com/android/platform_frameworks_base/blob/android-4.3_r2/core/java/android/bluetooth/BluetoothDevice.java#L1037).
Now, when I receive that exception, I instantiate a fallback BluetoothSocket, similar to the source code below. As you can see, invoking the hidden method createRfcommSocket via reflections. I have no clue why this method is hidden. The source code defines it as public though...
Class<?> clazz = tmp.getRemoteDevice().getClass();
Class<?>[] paramTypes = new Class<?>[] {Integer.TYPE};
Method m = clazz.getMethod("createRfcommSocket", paramTypes);
Object[] params = new Object[] {Integer.valueOf(1)};
fallbackSocket = (BluetoothSocket) m.invoke(tmp.getRemoteDevice(), params);
fallbackSocket.connect();
connect() then does not fail any longer. I have experienced a few issues still. Basically, this sometimes blocks and fails. Rebooting the SPP-Device (plug off / plug in) helps in such cases. Sometimes I also get another Pairing request after connect() even when the device is already bonded.
UPDATE:
here is a complete class, containing some nested classes. for a real implementation these could be held as seperate classes.
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.util.List;
import java.util.UUID;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.util.Log;
public class BluetoothConnector {
private BluetoothSocketWrapper bluetoothSocket;
private BluetoothDevice device;
private boolean secure;
private BluetoothAdapter adapter;
private List<UUID> uuidCandidates;
private int candidate;
/**
* #param device the device
* #param secure if connection should be done via a secure socket
* #param adapter the Android BT adapter
* #param uuidCandidates a list of UUIDs. if null or empty, the Serial PP id is used
*/
public BluetoothConnector(BluetoothDevice device, boolean secure, BluetoothAdapter adapter,
List<UUID> uuidCandidates) {
this.device = device;
this.secure = secure;
this.adapter = adapter;
this.uuidCandidates = uuidCandidates;
if (this.uuidCandidates == null || this.uuidCandidates.isEmpty()) {
this.uuidCandidates = new ArrayList<UUID>();
this.uuidCandidates.add(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
}
}
public BluetoothSocketWrapper connect() throws IOException {
boolean success = false;
while (selectSocket()) {
adapter.cancelDiscovery();
try {
bluetoothSocket.connect();
success = true;
break;
} catch (IOException e) {
//try the fallback
try {
bluetoothSocket = new FallbackBluetoothSocket(bluetoothSocket.getUnderlyingSocket());
Thread.sleep(500);
bluetoothSocket.connect();
success = true;
break;
} catch (FallbackException e1) {
Log.w("BT", "Could not initialize FallbackBluetoothSocket classes.", e);
} catch (InterruptedException e1) {
Log.w("BT", e1.getMessage(), e1);
} catch (IOException e1) {
Log.w("BT", "Fallback failed. Cancelling.", e1);
}
}
}
if (!success) {
throw new IOException("Could not connect to device: "+ device.getAddress());
}
return bluetoothSocket;
}
private boolean selectSocket() throws IOException {
if (candidate >= uuidCandidates.size()) {
return false;
}
BluetoothSocket tmp;
UUID uuid = uuidCandidates.get(candidate++);
Log.i("BT", "Attempting to connect to Protocol: "+ uuid);
if (secure) {
tmp = device.createRfcommSocketToServiceRecord(uuid);
} else {
tmp = device.createInsecureRfcommSocketToServiceRecord(uuid);
}
bluetoothSocket = new NativeBluetoothSocket(tmp);
return true;
}
public static interface BluetoothSocketWrapper {
InputStream getInputStream() throws IOException;
OutputStream getOutputStream() throws IOException;
String getRemoteDeviceName();
void connect() throws IOException;
String getRemoteDeviceAddress();
void close() throws IOException;
BluetoothSocket getUnderlyingSocket();
}
public static class NativeBluetoothSocket implements BluetoothSocketWrapper {
private BluetoothSocket socket;
public NativeBluetoothSocket(BluetoothSocket tmp) {
this.socket = tmp;
}
#Override
public InputStream getInputStream() throws IOException {
return socket.getInputStream();
}
#Override
public OutputStream getOutputStream() throws IOException {
return socket.getOutputStream();
}
#Override
public String getRemoteDeviceName() {
return socket.getRemoteDevice().getName();
}
#Override
public void connect() throws IOException {
socket.connect();
}
#Override
public String getRemoteDeviceAddress() {
return socket.getRemoteDevice().getAddress();
}
#Override
public void close() throws IOException {
socket.close();
}
#Override
public BluetoothSocket getUnderlyingSocket() {
return socket;
}
}
public class FallbackBluetoothSocket extends NativeBluetoothSocket {
private BluetoothSocket fallbackSocket;
public FallbackBluetoothSocket(BluetoothSocket tmp) throws FallbackException {
super(tmp);
try
{
Class<?> clazz = tmp.getRemoteDevice().getClass();
Class<?>[] paramTypes = new Class<?>[] {Integer.TYPE};
Method m = clazz.getMethod("createRfcommSocket", paramTypes);
Object[] params = new Object[] {Integer.valueOf(1)};
fallbackSocket = (BluetoothSocket) m.invoke(tmp.getRemoteDevice(), params);
}
catch (Exception e)
{
throw new FallbackException(e);
}
}
#Override
public InputStream getInputStream() throws IOException {
return fallbackSocket.getInputStream();
}
#Override
public OutputStream getOutputStream() throws IOException {
return fallbackSocket.getOutputStream();
}
#Override
public void connect() throws IOException {
fallbackSocket.connect();
}
#Override
public void close() throws IOException {
fallbackSocket.close();
}
}
public static class FallbackException extends Exception {
/**
*
*/
private static final long serialVersionUID = 1L;
public FallbackException(Exception e) {
super(e);
}
}
}
well, i had the same problem with my code, and it's because since android 4.2 bluetooth stack has changed. so my code was running fine on devices with android < 4.2 , on the other devices i was getting the famous exception "read failed, socket might closed or timeout, read ret: -1"
The problem is with the socket.mPort parameter. When you create your socket using socket = device.createRfcommSocketToServiceRecord(SERIAL_UUID); , the mPort gets integer value "-1", and this value seems doesn't work for android >=4.2 , so you need to set it to "1". The bad news is that createRfcommSocketToServiceRecord only accepts UUID as parameter and not mPort so we have to use other aproach. The answer posted by #matthes also worked for me, but i simplified it: socket =(BluetoothSocket) device.getClass().getMethod("createRfcommSocket", new Class[] {int.class}).invoke(device,1);. We need to use both socket attribs , the second one as a fallback.
So the code is (for connecting to a SPP on an ELM327 device):
BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
if (btAdapter.isEnabled()) {
SharedPreferences prefs_btdev = getSharedPreferences("btdev", 0);
String btdevaddr=prefs_btdev.getString("btdevaddr","?");
if (btdevaddr != "?")
{
BluetoothDevice device = btAdapter.getRemoteDevice(btdevaddr);
UUID SERIAL_UUID = UUID.fromString("00001101-0000-1000-8000-00805f9b34fb"); // bluetooth serial port service
//UUID SERIAL_UUID = device.getUuids()[0].getUuid(); //if you don't know the UUID of the bluetooth device service, you can get it like this from android cache
BluetoothSocket socket = null;
try {
socket = device.createRfcommSocketToServiceRecord(SERIAL_UUID);
} catch (Exception e) {Log.e("","Error creating socket");}
try {
socket.connect();
Log.e("","Connected");
} catch (IOException e) {
Log.e("",e.getMessage());
try {
Log.e("","trying fallback...");
socket =(BluetoothSocket) device.getClass().getMethod("createRfcommSocket", new Class[] {int.class}).invoke(device,1);
socket.connect();
Log.e("","Connected");
}
catch (Exception e2) {
Log.e("", "Couldn't establish Bluetooth connection!");
}
}
}
else
{
Log.e("","BT device not selected");
}
}
First, if you need to talk to a bluetooth 2.x device, this documentation states that :
Hint: If you are connecting to a Bluetooth serial board then try using
the well-known SPP UUID 00001101-0000-1000-8000-00805F9B34FB. However
if you are connecting to an Android peer then please generate your own
unique UUID.
I didn't think that it would work, but only by replacing the UUID with 00001101-0000-1000-8000-00805F9B34FB it works. However, this code seems to handle the problem of SDK version, and you can just replace the function device.createRfcommSocketToServiceRecord(mMyUuid); with tmp = createBluetoothSocket(mmDevice); after defining the following method :
private BluetoothSocket createBluetoothSocket(BluetoothDevice device)
throws IOException {
if(Build.VERSION.SDK_INT >= 10){
try {
final Method m = device.getClass().getMethod("createInsecureRfcommSocketToServiceRecord", new Class[] { UUID.class });
return (BluetoothSocket) m.invoke(device, mMyUuid);
} catch (Exception e) {
Log.e(TAG, "Could not create Insecure RFComm Connection",e);
}
}
return device.createRfcommSocketToServiceRecord(mMyUuid);
}
The source code isn't mine, but comes from this website.
I had the same symptoms as described here. I could connect once to a bluetooth printer but subsequent connects failed with "socket closed" no matter what I did.
I found it a bit strange that the workarounds described here would be necessary. After going through my code I found that I had forgot to close the socket's InputStream and OutputSteram and not terminated the ConnectedThreads properly.
The ConnectedThread I use is the same as in the example here:
http://developer.android.com/guide/topics/connectivity/bluetooth.html
Note that ConnectThread and ConnectedThread are two different classes.
Whatever class that starts the ConnectedThread must call interrupt() and cancel() on the thread.
I added mmInStream.close() and mmOutStream.close() in the ConnectedTread.cancel() method.
After closing the threads/streams/sockets properly I could create new sockets without any problem.
On newer versions of Android, I was receiving this error because the adapter was still discovering when I attempted to connect to the socket. Even though I called the cancelDiscovery method on the Bluetooth adapter, I had to wait until the callback to the BroadcastReceiver's onReceive() method was called with the action BluetoothAdapter.ACTION_DISCOVERY_FINISHED.
Once I waited for the adapter to stop discovery, then the connect call on the socket succeeded.
Well, I have actually found the problem.
The most people who try to make a connection using socket.Connect(); get an exception called Java.IO.IOException: read failed, socket might closed, read ret: -1.
In some cases it also depends on your Bluetooth device, because there are two different types of Bluetooth, namely BLE (low energy) and Classic.
If you want to check the type of your Bluetooth device is, here's the code:
String checkType;
var listDevices = BluetoothAdapter.BondedDevices;
if (listDevices.Count > 0)
{
foreach (var btDevice in listDevices)
{
if(btDevice.Name == "MOCUTE-032_B52-CA7E")
{
checkType = btDevice.Type.ToString();
Console.WriteLine(checkType);
}
}
}
I've been trying for days to solve the problem, but since today I have found the problem. The solution from #matthes has unfortunately still a few issues as he said already, but here's my solution.
At the moment I work in Xamarin Android, but this should also work for other platforms.
SOLUTION
If there is more than one paired device, then you should remove the other paired devices. So keep only the one that you want to connect (see the right image).
In the left image you see that I have two paired devices, namely "MOCUTE-032_B52-CA7E" and "Blue Easy". That's the issue, but I have no idea why that problem occurs. Maybe the Bluetooth protocol is trying to get some information from another Bluetooth device.
However, the socket.Connect(); works great right now, without any problems. So I just wanted to share this, because that error is really annoying.
Good luck!
In case somebody is having issues with Kotlin, I had to follow the accepted answer with some variations:
fun print(view: View, text: String) {
var adapter = BluetoothAdapter.getDefaultAdapter();
var pairedDevices = adapter.getBondedDevices()
var uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")
if (pairedDevices.size > 0) {
for (device in pairedDevices) {
var s = device.name
if (device.getName().equals(printerName, ignoreCase = true)) {
Thread {
var socket = device.createInsecureRfcommSocketToServiceRecord(uuid)
var clazz = socket.remoteDevice.javaClass
var paramTypes = arrayOf<Class<*>>(Integer.TYPE)
var m = clazz.getMethod("createRfcommSocket", *paramTypes)
var fallbackSocket = m.invoke(socket.remoteDevice, Integer.valueOf(1)) as BluetoothSocket
try {
fallbackSocket.connect()
var stream = fallbackSocket.outputStream
stream.write(text.toByteArray(Charset.forName("UTF-8")))
} catch (e: Exception) {
e.printStackTrace()
Snackbar.make(view, "An error occurred", Snackbar.LENGTH_SHORT).show()
}
}.start()
}
}
}
}
Hope it helps
You put
registerReceiver(receiver, new IntentFilter("android.bleutooth.device.action.UUID"));
with "bluetooth" spelled "bleutooth".
Bluetooth devices can operate in both classic and LE mode at the same time. Sometimes they use a different MAC address depending on which way you are connecting. Calling socket.connect() is using Bluetooth Classic, so you have to make sure the device you got when you scanned was really a classic device.
It's easy to filter for only Classic devices, however:
if(BluetoothDevice.DEVICE_TYPE_LE == device.getType()){
//socket.connect()
}
Without this check, it's a race condition as to whether a hybrid scan will give you the Classic device or the BLE device first. It may appear as intermittent inability to connect, or as certain devices being able to connect reliably while others seemingly never can.
i also faced with this problem,you could solve it in 2 ways , as mentioned earlier use reflection to create the socket
Second one is,
client is looking for a server with given UUID and if your server isn't running parallel to client then this happens.
Create a server with given client UUID and then listen and accept the client from server side.It will work.
Even i had the same problem ,finally understand my issue , i was trying to connect from (out of range) Bluetooth coverage range.
I ran into this problem and fixed it by closing the input and output streams before closing the socket. Now I can disconnect and connect again with no issues.
https://stackoverflow.com/a/3039807/5688612
In Kotlin:
fun disconnect() {
bluetoothSocket.inputStream.close()
bluetoothSocket.outputStream.close()
bluetoothSocket.close()
}
If another part of your code has already made a connection with the same socket and UUID, you get this error.
I've had this problem and the solution was to use the special magic GUID.
val id: UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB") // Any other GUID doesn't work.
val device: BluetoothDevice = bta!!.bondedDevices.first { z -> z.name == deviceName }
bts = device.createRfcommSocketToServiceRecord(id) // mPort is -1
bts?.connect()
// Start processing thread.
I suspect that these are the UUIDs that work:
var did: Array<ParcelUuid?> = device.uuids
However, I have not tried them all.
By adding filter action my problem resolved
// Register for broadcasts when a device is discovered
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(BluetoothDevice.ACTION_FOUND);
intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
registerReceiver(mReceiver, intentFilter);
I have also receive the same IOException, but I find the Android system demo: "BluetoothChat" project is worked. I determined the problem is the UUID.
So i replace my UUID.fromString("00001001-0000-1000-8000-00805F9B34FB") to UUID.fromString("8ce255c0-200a-11e0-ac64-0800200c9a66") and it worked most scene,only sometimes need to restart the Bluetooth device;

Facebook messenger app using asmack android xmpp

I was trying to fetch facebook friends and send message using asmack library over XMPP.I took reference from this site for facebook messaging.But after executing the code below i got exception.
public class MyActivity extends Activity {
public int state = 0;
private static final String TAG = "MyActivity";
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
new Thread(new Runnable() {
public void run() {
//XMPPConnection xmpp = new XMPPConnection("jabber.iitsp.com");
XMPPConnection xmpp = new XMPPConnection("chat.facebook.com");
try {
xmpp.connect();
// for other jabber accounts, truncate after the #
//xmpp.login("username", "password");
// for gtalk / gmail, include the #
xmpp.login("fb-username#chat.facebook.com", "your-fb-password");
} catch (XMPPException e) {
Log.v(TAG, "Failed to connect to " + xmpp.getHost());
e.printStackTrace();
}
ChatManager chatmanager = xmpp.getChatManager();
Chat newChat = chatmanager.createChat("friend#chat.facebook.com", new MessageListener() {
// THIS CODE NEVER GETS CALLED FOR SOME REASON
public void processMessage(Chat chat, Message message) {
try {
Log.v(TAG, "Got:" + message.getBody());
chat.sendMessage(message.getBody());
} catch (XMPPException e) {
Log.v(TAG, "Couldn't respond:" + e);
}
Log.v(TAG, message.toString());
}
});
// Send something to friend#chat.facebook.com
try {
newChat.sendMessage("my message");
} catch (XMPPException e) {
Log.v(TAG, "couldn't send:" + e.toString());
}
// Accept only messages from friend#chat.facebook.com
PacketFilter filter
= new AndFilter(new PacketTypeFilter(Message.class),
new FromContainsFilter("friend#chat.facebook.com"));
// Collect these messages
PacketCollector collector = xmpp.createPacketCollector(filter);
while(true) {
Packet packet = collector.nextResult();
if (packet instanceof Message) {
Message msg = (Message) packet;
// Process message
Log.v(TAG, "Got message:" + msg.getBody());
}
}
}
}).start();
//setContentView(this);
}}
I got following errors during execution of the code:-
1) java.security.KeyStoreException: java.security.NoSuchAlgorithmException: KeyStore jks implementation not found
2) SASL authentication failed using mechanism PLAIN:
Then my program crashed
java.lang.IllegalStateException: Not connected to server.
Any help would be appreciated.

Invoke sendMessage method from second activity

I'm doing some dev for a Chromecast sender app, and I have this method in my MainActivity class:
public void sendMessage(String message) {
if (mApiClient != null && mHelloWorldChannel != null) {
try {
Cast.CastApi.sendMessage(mApiClient, mHelloWorldChannel.getNamespace(), message)
.setResultCallback(
new ResultCallback<Status>() {
#Override
public void onResult(Status result) {
if (!result.isSuccess()) {
Log.e(TAG, "Sending message failed");
} else {
System.out.println("Message sent!");
}
}
});
} catch (Exception e) {
Log.e(TAG, "Exception while sending message", e);
}
} else {
if (mApiClient == null) {
System.out.println("apiClient null");
}
if (mHelloWorldChannel == null) {
System.out.println("mHello null");
}
}
}
Every works dandy and I can send messages to my Chromecast and do nifty things with them when they get there. However, I use a number of other Activities in my app, and I'd like to be able to send messages to the Chromecast from those as well.
That said, what is the best way to access this method from a second activity?
If you will define your method static one then you can call it like this:
ClassName.sendMessage(String message);
If it is not static one, you will need to create an object for it.
ClassName c= new ClassName(SomeconstructorParams);
c.sendMessage(String message);

Categories

Resources