12de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhoupackage com.google.android.experimental.bttraffic;
22de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
32de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhouimport android.app.Service;
42de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhouimport android.bluetooth.BluetoothAdapter;
52de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhouimport android.bluetooth.BluetoothDevice;
62de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhouimport android.bluetooth.BluetoothServerSocket;
72de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhouimport android.bluetooth.BluetoothSocket;
82de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhouimport android.content.Intent;
92de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhouimport android.os.Bundle;
102de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhouimport android.os.IBinder;
112de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhouimport android.os.SystemClock;
122de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhouimport android.util.Log;
132de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
142de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhouimport java.io.Closeable;
152de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhouimport java.io.IOException;
162de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhouimport java.io.InputStream;
172de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhouimport java.io.OutputStream;
182de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhouimport java.lang.Exception;
192de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhouimport java.lang.Runtime;
202de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhouimport java.lang.RuntimeException;
212de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhouimport java.lang.Process;
222de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhouimport java.nio.ByteBuffer;
232de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhouimport java.util.Random;
242de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhouimport java.util.Set;
252de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhouimport java.util.UUID;
262de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
272de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhoupublic class BTtraffic extends Service {
282de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou    public static final String TAG = "bttraffic";
292de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou    static final String SERVICE_NAME = "bttraffic";
302de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou    static final String SYS_SERVICE_NAME = "com.android.bluetooth";
312de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou    static final UUID SERVICE_UUID = UUID.fromString("5e8945b0-1234-5432-a5e2-0800200c9a67");
322de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou    volatile Thread mWorkerThread;
332de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou    volatile boolean isShuttingDown = false;
342de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou    volatile boolean isServer = false;
352de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
362de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou    public BTtraffic() {}
372de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
382de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou    static void safeClose(Closeable closeable) {
392de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        try {
402de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            closeable.close();
412de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        } catch (IOException e) {
422de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            Log.d(TAG, "Unable to close resource.\n");
432de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        }
442de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou    }
452de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
462de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou    @Override
472de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou    public int onStartCommand(Intent intent, int flags, int startId) {
482de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        if (intent == null) {
492de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            stopSelf();
502de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            return 0;
512de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        }
522de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        if ("stop".equals(intent.getAction())) {
532de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            stopService();
542de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        } else if ("start".equals(intent.getAction())) {
552de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            startWorker(intent);
562de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        } else {
572de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            Log.d(TAG, "unknown action: + " + intent.getAction());
582de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        }
592de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        return 0;
602de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou    }
612de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
622de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou    private void startWorker(Intent intent) {
632de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        if (mWorkerThread != null) {
642de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            Log.d(TAG, "worker thread already active");
652de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            return;
662de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        }
672de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        isShuttingDown = false;
682de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        String remoteAddr = intent.getStringExtra("addr");
692de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        Log.d(TAG, "startWorker: addr=" + remoteAddr);
702de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        Runnable worker =
712de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                remoteAddr == null
722de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                        ? new ListenerRunnable(this, intent)
732de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                        : new SenderRunnable(this, remoteAddr, intent);
742de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        isServer = remoteAddr == null ? true: false;
752de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        mWorkerThread = new Thread(worker, "BTtrafficWorker");
762de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        try {
772de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            startMonitor();
782de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            Log.d(TAG, "Monitor service started");
792de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            mWorkerThread.start();
802de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            Log.d(TAG, "Worker thread started");
812de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        } catch (Exception e) {
822de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            Log.d(TAG, "Failed to start service", e);
832de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        }
842de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou    }
852de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
862de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou    private void startMonitor()
872de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            throws Exception {
882de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        if (isServer) {
892de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            Log.d(TAG, "Start monitor on server");
902de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            String[] startmonitorCmd = {
912de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                    "/system/bin/am",
922de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                    "startservice",
932de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                    "-a", "start",
942de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                    "-e", "java", SERVICE_NAME,
952de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                    "-e", "hal", SYS_SERVICE_NAME,
962de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                    "com.google.android.experimental.svcmonitor/.SvcMonitor"
972de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            };
982de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            Process ps = new ProcessBuilder()
992de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                    .command(startmonitorCmd)
1002de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                    .redirectErrorStream(true)
1012de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                    .start();
1022de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        } else {
1032de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            Log.d(TAG, "No need to start SvcMonitor on client");
1042de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        }
1052de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou    }
1062de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
1072de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou    private void stopMonitor()
1082de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            throws Exception {
1092de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        if (isServer) {
1102de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            Log.d(TAG, "StopMonitor on server");
1112de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            String[] stopmonitorCmd = {
1122de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                    "/system/bin/am",
1132de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                    "startservice",
1142de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                    "-a", "stop",
1152de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                    "com.google.android.experimental.svcmonitor/.SvcMonitor"
1162de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            };
1172de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            Process ps = new ProcessBuilder()
1182de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                    .command(stopmonitorCmd)
1192de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                    .redirectErrorStream(true)
1202de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                    .start();
1212de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        } else {
1222de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            Log.d(TAG, "No need to stop Svcmonitor on client");
1232de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        }
1242de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou    }
1252de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
1262de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou    public void stopService() {
1272de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        if (mWorkerThread == null) {
1282de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            Log.d(TAG, "no active thread");
1292de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            return;
1302de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        }
1312de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
1322de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        isShuttingDown = true;
1332de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
1342de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        try {
1352de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            stopMonitor();
1362de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        } catch (Exception e) {
1372de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            Log.d(TAG, "Unable to stop SvcMonitor!", e);
1382de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        }
1392de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
1402de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        if (Thread.currentThread() != mWorkerThread) {
1412de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            mWorkerThread.interrupt();
1422de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            Log.d(TAG, "Interrupting thread");
1432de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            try {
1442de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                mWorkerThread.join();
1452de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            } catch (InterruptedException e) {
1462de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                Log.d(TAG, "Unable to join thread!");
1472de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            }
1482de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        }
1492de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
1502de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        mWorkerThread = null;
1512de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        stopSelf();
1522de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        Log.d(TAG, "Service stopped");
1532de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou    }
1542de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
1552de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou    @Override
1562de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou    public void onDestroy() {
1572de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        super.onDestroy();
1582de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou    }
1592de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
1602de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou    @Override
1612de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou    public IBinder onBind(Intent intent) {
1622de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        throw new UnsupportedOperationException("Not yet implemented");
1632de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou    }
1642de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
1652de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou    public static class ListenerRunnable implements Runnable {
1662de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        private final BTtraffic bttraffic;
1672de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        private final boolean sendAck;
1682de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        private Intent intent;
1692de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        private final int maxbuffersize = 20 * 1024 * 1024;
1702de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
1712de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        public ListenerRunnable(BTtraffic bttraffic, Intent intent) {
1722de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            this.bttraffic = bttraffic;
1732de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            this.sendAck = intent.getBooleanExtra("ack", true);
1742de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            this.intent = intent;
1752de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        }
1762de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
1772de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        @Override
1782de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        public void run() {
1792de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            BluetoothServerSocket serverSocket;
1802de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
1812de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            try {
1822de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                Log.d(TAG, "getting server socket");
1832de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                serverSocket = BluetoothAdapter.getDefaultAdapter()
1842de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                        .listenUsingInsecureRfcommWithServiceRecord(
1852de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                                SERVICE_NAME, SERVICE_UUID);
1862de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            } catch (IOException e) {
1872de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                Log.d(TAG, "error creating server socket, stopping thread");
1882de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                bttraffic.stopService();
1892de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                return;
1902de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            }
1912de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
1922de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            Log.d(TAG, "got server socket, starting accept loop");
1932de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            BluetoothSocket socket = null;
1942de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            try {
1952de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                Log.d(TAG, "accepting");
1962de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                socket = serverSocket.accept();
1972de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
1982de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                if (!Thread.interrupted()) {
1992de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                    Log.d(TAG, "accepted, listening");
2002de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                    doListening(socket.getInputStream(), socket.getOutputStream());
2012de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                    Log.d(TAG, "listen finished");
2022de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                }
2032de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            } catch (IOException e) {
2042de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                Log.d(TAG, "error while accepting or listening", e);
2052de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            } finally {
2062de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                Log.d(TAG, "Linster interruped");
2072de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                Log.d(TAG, "closing socket and stopping service");
2082de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                safeClose(serverSocket);
2092de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                safeClose(socket);
2102de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                if (!bttraffic.isShuttingDown)
2112de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                    bttraffic.stopService();
2122de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            }
2132de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
2142de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        }
2152de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
2162de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        private void doListening(InputStream inputStream, OutputStream outputStream)
2172de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                throws IOException {
2182de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            ByteBuffer byteBuffer = ByteBuffer.allocate(maxbuffersize);
2192de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
2202de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            while (!Thread.interrupted()) {
2212de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                readBytesIntoBuffer(inputStream, byteBuffer, 4);
2222de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                byteBuffer.flip();
2232de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                int length = byteBuffer.getInt();
2242de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                if (Thread.interrupted())
2252de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                    break;
2262de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                readBytesIntoBuffer(inputStream, byteBuffer, length);
2272de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
2282de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                if (sendAck)
2292de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                    outputStream.write(0x55);
2302de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            }
2312de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        }
2322de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
2332de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        void readBytesIntoBuffer(InputStream inputStream, ByteBuffer byteBuffer, int numToRead)
2342de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                throws IOException {
2352de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            byteBuffer.clear();
2362de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            while (true) {
2372de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                int position = byteBuffer.position();
2382de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                int remaining = numToRead - position;
2392de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                if (remaining == 0) {
2402de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                    break;
2412de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                }
2422de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                int count = inputStream.read(byteBuffer.array(), position, remaining);
2432de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                if (count < 0) {
2442de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                    throw new IOException("read the EOF");
2452de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                }
2462de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                byteBuffer.position(position + count);
2472de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            }
2482de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        }
2492de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou    }
2502de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
2512de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou    public static class SenderRunnable implements Runnable {
2522de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        private final BTtraffic bttraffic;
2532de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        private final String remoteAddr;
2542de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        private final int pkgsize, period;
2552de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        private final int defaultpkgsize = 1024;
2562de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        private final int defaultperiod = 5000;
2572de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        private static ByteBuffer lengthBuffer = ByteBuffer.allocate(4);
2582de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
2592de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        public SenderRunnable(BTtraffic bttraffic, String remoteAddr, Intent intent) {
2602de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            this.bttraffic = bttraffic;
2612de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            this.remoteAddr = remoteAddr;
2622de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            this.pkgsize = intent.getIntExtra("size", defaultpkgsize);
2632de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            this.period = intent.getIntExtra("period", defaultperiod);
2642de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        }
2652de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
2662de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        @Override
2672de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        public void run() {
2682de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            BluetoothDevice device = null;
2692de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            try {
2702de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(remoteAddr);
2712de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            } catch (IllegalArgumentException e) {
2722de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                Log.d(TAG, "Invalid BT MAC address!\n");
2732de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            }
2742de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            if (device == null) {
2752de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                Log.d(TAG, "can't find matching device, stopping thread and service");
2762de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                bttraffic.stopService();
2772de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                return;
2782de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            }
2792de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
2802de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            BluetoothSocket socket = null;
2812de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            try {
2822de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                Log.d(TAG, "connecting to device with MAC addr: " + remoteAddr);
2832de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                socket = device.createInsecureRfcommSocketToServiceRecord(SERVICE_UUID);
2842de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                socket.connect();
2852de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                Log.d(TAG, "connected, starting to send");
2862de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                doSending(socket.getOutputStream());
2872de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                Log.d(TAG, "send stopped, stopping service");
2882de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            } catch (Exception e) {
2892de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                Log.d(TAG, "error while sending", e);
2902de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            } finally {
2912de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                Log.d(TAG, "finishing, closing thread and service");
2922de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                safeClose(socket);
2932de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                if (!bttraffic.isShuttingDown)
2942de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                    bttraffic.stopService();
2952de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            }
2962de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        }
2972de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
2982de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        private void doSending(OutputStream outputStream) throws IOException {
2992de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            Log.w(TAG, "doSending");
3002de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            try {
3012de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                Random random = new Random(System.currentTimeMillis());
3022de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
3032de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                byte[] bytes = new byte[pkgsize];
3042de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                random.nextBytes(bytes);
3052de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                while (!Thread.interrupted()) {
3062de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                    writeBytes(outputStream, bytes.length);
3072de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                    outputStream.write(bytes, 0, bytes.length);
3082de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                    if (period < 0)
3092de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                        break;
3102de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                    if (period == 0)
3112de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                        continue;
3122de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
3132de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                    SystemClock.sleep(period);
3142de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                }
3152de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                Log.d(TAG, "Sender interrupted");
3162de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            } catch (IOException e) {
3172de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou                Log.d(TAG, "doSending got error", e);
3182de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            }
3192de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        }
3202de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
3212de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        private static void writeBytes(OutputStream outputStream, int value) throws IOException {
3222de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            lengthBuffer.putInt(value);
3232de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            lengthBuffer.flip();
3242de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou            outputStream.write(lengthBuffer.array(), lengthBuffer.position(), lengthBuffer.limit());
3252de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou        }
3262de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou    }
3272de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou
3282de4b74d80edd8a8ee063980c6eb608369c5f81aYuchao Zhou}
329