/********************************************************************************* * TotalCross Software Development Kit * * Copyright (C) 2000-2012 SuperWaba Ltda. * * All Rights Reserved * * * * This library and virtual machine is distributed in the hope that it will * * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * * * This file is covered by the GNU LESSER GENERAL PUBLIC LICENSE VERSION 3.0 * * A copy of this license is located in file license.txt at the root of this * * SDK or can be downloaded here: * * http://www.gnu.org/licenses/lgpl-3.0.txt * * * *********************************************************************************/ package totalcross.android.compat; import totalcross.*; import java.io.*; import java.util.*; import android.bluetooth.*; import android.content.*; import android.os.*; public class Level5Impl extends Level5 { //private static final UUID MY_UUID = UUID.fromString("e3b9f92c-3226-4ccb-9e0a-218695bd402c"); private static final UUID SPP_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); BluetoothAdapter btAdapter; public Level5Impl() { } public void processMessage(Bundle b) { int type = b.getInt("subtype"); if (btAdapter == null) try { btAdapter = BluetoothAdapter.getDefaultAdapter(); } catch (Exception e) { AndroidUtils.handleException(e,false); } if (type == BT_IS_SUPPORTED && btAdapter == null) setResponse(ERROR, null); else try { switch (type) { case BT_IS_SUPPORTED: // setResponse(btAdapter != null, null); break; case BT_ACTIVATE: // setResponse(btAdapter.isEnabled() || btAdapter.enable(),null); break; case BT_DEACTIVATE: // setResponse(!btAdapter.isEnabled() || btAdapter.disable(),null); break; case BT_GET_UNPAIRED_DEVICES: btGetUnpairedDevices(); break; case BT_GET_PAIRED_DEVICES: btGetPairedDevices(); break; case BT_MAKE_DISCOVERABLE: // btMakeDiscoverable(); break; case BT_CONNECT: btConnect(b.getString("param")); break; case BT_READ: btReadWrite(true, b.getString("param"), b.getByteArray("bytes"), b.getInt("ofs"), b.getInt("len")); break; case BT_WRITE: btReadWrite(false, b.getString("param"), b.getByteArray("bytes"), b.getInt("ofs"), b.getInt("len")); break; case BT_CLOSE: btClose(b.getString("param")); break; case BT_IS_RADIO_ON: // setResponse(btAdapter.getState() == BluetoothAdapter.STATE_ON, null); break; case BT_IS_DISCOVERABLE: // setResponse(btAdapter.getScanMode() == BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE, null); break; case BT_SERVER_ACCEPT: btserverAccept(b.getString("param")); break; case BT_SERVER_CLOSE: btserverClose(); break; } } catch (Exception e) { AndroidUtils.handleException(e,false); setResponse(false,null); } } private void btReadWrite(boolean isRead, String btc, byte[] byteArray, int ofs, int count) throws Exception { AndroidUtils.debug("btReadWrite("+isRead+","+btc+","+ofs+","+count); BluetoothSocket sock = htbt.get(btc); if (sock == null) throw new IOException("socket for device "+btc+" not found on hashtable"); if (isRead) { InputStream is = sock.getInputStream(); AndroidUtils.debug("read.is: "+is); if (is == null) setResponse(ERROR,null); else { int n = is.read(byteArray, ofs, count); setResponse(n,null); } } else { OutputStream os = sock.getOutputStream(); if (os == null) setResponse(ERROR,null); else { os.write(byteArray, ofs, count); os.flush(); setResponse(count,null); } } } private void btClose(String btc) { BluetoothSocket sock = htbt.get(btc); if (sock != null) { try {sock.close();} catch (Exception e) {AndroidUtils.handleException(e,false);} htbt.remove(btc); } setResponse(sock != null, null); } Hashtable<String,BluetoothSocket> htbt = new Hashtable<String,BluetoothSocket>(5); /* private boolean isPaired(String addr) { println("listing paired devices and searching for "+addr); Set<BluetoothDevice> pairedDevices = btAdapter.getBondedDevices(); // If there are paired devices for (BluetoothDevice dev : pairedDevices) { String a = dev.getAddress().replace(":",""); println("Found "+a); if (a.equals(addr)) return true; } return false; } private void restartBT() { println("Disabling..."); btAdapter.disable(); while (btAdapter.getState() != BluetoothAdapter.STATE_OFF) try {Thread.sleep(100);} catch (Exception eee) {} println("Enabling..."); btAdapter.enable(); while (btAdapter.getState() != BluetoothAdapter.STATE_ON) { println("Waiting enable: "+btAdapter.getState()); try {Thread.sleep(500);} catch (Exception eee) {} } } */ private void btConnect(String addr) throws Exception { boolean unsecure = false; if (addr.startsWith("*")) { println("unsecure connection"); unsecure = true; addr = addr.substring(1); } BluetoothSocket sock = htbt.get(addr); if (sock == null) { if (btAdapter.isDiscovering()) // unlikely to occur but a good practice btAdapter.cancelDiscovery(); String formattedAddr = formatAddress(addr); BluetoothDevice device = btAdapter.getRemoteDevice(formattedAddr); sock = unsecure ? device.createInsecureRfcommSocketToServiceRecord(SPP_UUID) : device.createRfcommSocketToServiceRecord(SPP_UUID); while (true) try { sock.connect(); break; } catch (java.io.IOException e) { AndroidUtils.handleException(e,false); String msg = e.getMessage(); if (msg != null && msg.indexOf("discovery failed") >= 0) // invalid password { if (device.getBondState() == BluetoothDevice.BOND_BONDING) { println("STILL BONDING"); for (int i = 0; i < 20 && device.getBondState() == BluetoothDevice.BOND_BONDING; i++) { println("Waiting "+(20-i)+" seconds before giving up."); try {Thread.sleep(1000);} catch (Exception ee) {} } if (device.getBondState() == BluetoothDevice.BOND_BONDED) { println("BONDED! TRYING AGAIN"); continue; } } setResponse(INVALID_PASSWORD,null); return; } else { //e.fillInStackTrace(); throw e; } } println("sock "+sock+" connected on device "+addr); htbt.put(unsecure ? "*"+addr : addr,sock); } setResponse(sock != null,null); } private static String formatAddress(String s) // 000780885D13 -> 00:07:80:88:5D:13 { try { char[] out = new char[17]; for (int j = 0, i = 0, k = 0; j < 6; j++) { out[k++] = s.charAt(i++); out[k++] = s.charAt(i++); if (j < 5) out[k++] = ':'; } return new String(out); } catch (StringIndexOutOfBoundsException sioobe) { println("Error formatting address '"+s+"'"); AndroidUtils.handleException(sioobe,false); return s; } } private void btMakeDiscoverable() { if (btAdapter.getScanMode() != BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) { Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE); discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300); Launcher4A.loader.startActivityForResult(discoverableIntent, BT_MAKE_DISCOVERABLE); } else setResponse(true,null); } Vector<String> devices = new Vector<String>(10); private void btGetPairedDevices() { devices.removeAllElements(); Set<BluetoothDevice> pairedDevices = btAdapter.getBondedDevices(); // If there are paired devices if (pairedDevices.size() > 0) for (BluetoothDevice device : pairedDevices) devices.add(device.getAddress().replace(":","")+"|"+device.getName()); String[] ret = null; if (devices.size() > 0) devices.copyInto(ret = new String[devices.size()]); setResponse(true,ret); } private static void println(String s) { AndroidUtils.debug("*** "+s); } private final BroadcastReceiver mReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); // When discovery finds a device if (BluetoothDevice.ACTION_FOUND.equals(action)) { // Get the BluetoothDevice object from the Intent BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); String name = device.getName(); String btd = device.getAddress().replace(":","")+"|"+name; if (name != null && !devices.contains(btd)) devices.add(btd); } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) { Launcher4A.loader.unregisterReceiver(this); String[] ret = null; if (devices.size() > 0) devices.copyInto(ret = new String[devices.size()]); setResponse(true,ret); } } }; private void btGetUnpairedDevices() { devices.removeAllElements(); Launcher4A.loader.registerReceiver(mReceiver, new IntentFilter(BluetoothDevice.ACTION_FOUND)); Launcher4A.loader.registerReceiver(mReceiver, new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)); btAdapter.startDiscovery(); } /////////////////// SERVER SOCKET METHODS //////////////////// class ServerSocketThread extends Thread { String uuid; public void run() { try { BluetoothSocket acceptedSocket = serverSocket.accept(); // blocks until a connection is accepted if (acceptedSocket != null) htbt.put(uuid,acceptedSocket); setResponse(acceptedSocket != null,null); } catch (Exception ee) { AndroidUtils.handleException(ee,false); } } } BluetoothServerSocket serverSocket; // only a single instance can be active on Android ServerSocketThread sst; private void btserverAccept(String uuid) throws Exception { boolean unsecure = false; if (uuid != null && uuid.startsWith("*")) { unsecure = true; uuid = uuid.equals("*") ? "" : uuid.substring(1); } // cleanup previous instances if (serverSocket != null) try {serverSocket.close();} catch (Exception e) {AndroidUtils.handleException(e,false);} if (sst != null && sst.isAlive()) sst.interrupt(); // create new ones UUID u = SPP_UUID;//UUID.fromString(uuid.replace("{","").replace("}","")); // "27648B4D-D854-5674-FA60E4F535E44AF7 // generate your own UUID at http://www.uuidgenerator.com serverSocket = unsecure ? btAdapter.listenUsingInsecureRfcommWithServiceRecord("MyBluetoothApp", u) : btAdapter.listenUsingRfcommWithServiceRecord("MyBluetoothApp", u); sst = new ServerSocketThread(); sst.uuid = uuid; // must be the original uuid sst.start(); } private void btserverClose() // uuid is currently being ignored, since only one serversocket instance is currently allowed { if (serverSocket != null) try { serverSocket.close(); // close the listening socket - does not close the connected client socket serverSocket = null; } catch (Exception e) { AndroidUtils.handleException(e,false); } setResponse(true,null); } }