Home | History | Annotate | Download | only in bttraffic
      1 package com.google.android.experimental.bttraffic;
      2 
      3 import android.app.Service;
      4 import android.bluetooth.BluetoothAdapter;
      5 import android.bluetooth.BluetoothDevice;
      6 import android.bluetooth.BluetoothServerSocket;
      7 import android.bluetooth.BluetoothSocket;
      8 import android.content.Intent;
      9 import android.os.Bundle;
     10 import android.os.IBinder;
     11 import android.os.SystemClock;
     12 import android.util.Log;
     13 
     14 import java.io.Closeable;
     15 import java.io.IOException;
     16 import java.io.InputStream;
     17 import java.io.OutputStream;
     18 import java.lang.Exception;
     19 import java.lang.Runtime;
     20 import java.lang.RuntimeException;
     21 import java.lang.Process;
     22 import java.nio.ByteBuffer;
     23 import java.util.Random;
     24 import java.util.Set;
     25 import java.util.UUID;
     26 
     27 public class BTtraffic extends Service {
     28     public static final String TAG = "bttraffic";
     29     static final String SERVICE_NAME = "bttraffic";
     30     static final String SYS_SERVICE_NAME = "com.android.bluetooth";
     31     static final UUID SERVICE_UUID = UUID.fromString("5e8945b0-1234-5432-a5e2-0800200c9a67");
     32     volatile Thread mWorkerThread;
     33     volatile boolean isShuttingDown = false;
     34     volatile boolean isServer = false;
     35 
     36     public BTtraffic() {}
     37 
     38     static void safeClose(Closeable closeable) {
     39         try {
     40             closeable.close();
     41         } catch (IOException e) {
     42             Log.d(TAG, "Unable to close resource.\n");
     43         }
     44     }
     45 
     46     @Override
     47     public int onStartCommand(Intent intent, int flags, int startId) {
     48         if (intent == null) {
     49             stopSelf();
     50             return 0;
     51         }
     52         if ("stop".equals(intent.getAction())) {
     53             stopService();
     54         } else if ("start".equals(intent.getAction())) {
     55             startWorker(intent);
     56         } else {
     57             Log.d(TAG, "unknown action: + " + intent.getAction());
     58         }
     59         return 0;
     60     }
     61 
     62     private void startWorker(Intent intent) {
     63         if (mWorkerThread != null) {
     64             Log.d(TAG, "worker thread already active");
     65             return;
     66         }
     67         isShuttingDown = false;
     68         String remoteAddr = intent.getStringExtra("addr");
     69         Log.d(TAG, "startWorker: addr=" + remoteAddr);
     70         Runnable worker =
     71                 remoteAddr == null
     72                         ? new ListenerRunnable(this, intent)
     73                         : new SenderRunnable(this, remoteAddr, intent);
     74         isServer = remoteAddr == null ? true: false;
     75         mWorkerThread = new Thread(worker, "BTtrafficWorker");
     76         try {
     77             startMonitor();
     78             Log.d(TAG, "Monitor service started");
     79             mWorkerThread.start();
     80             Log.d(TAG, "Worker thread started");
     81         } catch (Exception e) {
     82             Log.d(TAG, "Failed to start service", e);
     83         }
     84     }
     85 
     86     private void startMonitor()
     87             throws Exception {
     88         if (isServer) {
     89             Log.d(TAG, "Start monitor on server");
     90             String[] startmonitorCmd = {
     91                     "/system/bin/am",
     92                     "startservice",
     93                     "-a", "start",
     94                     "-e", "java", SERVICE_NAME,
     95                     "-e", "hal", SYS_SERVICE_NAME,
     96                     "com.google.android.experimental.svcmonitor/.SvcMonitor"
     97             };
     98             Process ps = new ProcessBuilder()
     99                     .command(startmonitorCmd)
    100                     .redirectErrorStream(true)
    101                     .start();
    102         } else {
    103             Log.d(TAG, "No need to start SvcMonitor on client");
    104         }
    105     }
    106 
    107     private void stopMonitor()
    108             throws Exception {
    109         if (isServer) {
    110             Log.d(TAG, "StopMonitor on server");
    111             String[] stopmonitorCmd = {
    112                     "/system/bin/am",
    113                     "startservice",
    114                     "-a", "stop",
    115                     "com.google.android.experimental.svcmonitor/.SvcMonitor"
    116             };
    117             Process ps = new ProcessBuilder()
    118                     .command(stopmonitorCmd)
    119                     .redirectErrorStream(true)
    120                     .start();
    121         } else {
    122             Log.d(TAG, "No need to stop Svcmonitor on client");
    123         }
    124     }
    125 
    126     public void stopService() {
    127         if (mWorkerThread == null) {
    128             Log.d(TAG, "no active thread");
    129             return;
    130         }
    131 
    132         isShuttingDown = true;
    133 
    134         try {
    135             stopMonitor();
    136         } catch (Exception e) {
    137             Log.d(TAG, "Unable to stop SvcMonitor!", e);
    138         }
    139 
    140         if (Thread.currentThread() != mWorkerThread) {
    141             mWorkerThread.interrupt();
    142             Log.d(TAG, "Interrupting thread");
    143             try {
    144                 mWorkerThread.join();
    145             } catch (InterruptedException e) {
    146                 Log.d(TAG, "Unable to join thread!");
    147             }
    148         }
    149 
    150         mWorkerThread = null;
    151         stopSelf();
    152         Log.d(TAG, "Service stopped");
    153     }
    154 
    155     @Override
    156     public void onDestroy() {
    157         super.onDestroy();
    158     }
    159 
    160     @Override
    161     public IBinder onBind(Intent intent) {
    162         throw new UnsupportedOperationException("Not yet implemented");
    163     }
    164 
    165     public static class ListenerRunnable implements Runnable {
    166         private final BTtraffic bttraffic;
    167         private final boolean sendAck;
    168         private Intent intent;
    169         private final int maxbuffersize = 20 * 1024 * 1024;
    170 
    171         public ListenerRunnable(BTtraffic bttraffic, Intent intent) {
    172             this.bttraffic = bttraffic;
    173             this.sendAck = intent.getBooleanExtra("ack", true);
    174             this.intent = intent;
    175         }
    176 
    177         @Override
    178         public void run() {
    179             BluetoothServerSocket serverSocket;
    180 
    181             try {
    182                 Log.d(TAG, "getting server socket");
    183                 serverSocket = BluetoothAdapter.getDefaultAdapter()
    184                         .listenUsingInsecureRfcommWithServiceRecord(
    185                                 SERVICE_NAME, SERVICE_UUID);
    186             } catch (IOException e) {
    187                 Log.d(TAG, "error creating server socket, stopping thread");
    188                 bttraffic.stopService();
    189                 return;
    190             }
    191 
    192             Log.d(TAG, "got server socket, starting accept loop");
    193             BluetoothSocket socket = null;
    194             try {
    195                 Log.d(TAG, "accepting");
    196                 socket = serverSocket.accept();
    197 
    198                 if (!Thread.interrupted()) {
    199                     Log.d(TAG, "accepted, listening");
    200                     doListening(socket.getInputStream(), socket.getOutputStream());
    201                     Log.d(TAG, "listen finished");
    202                 }
    203             } catch (IOException e) {
    204                 Log.d(TAG, "error while accepting or listening", e);
    205             } finally {
    206                 Log.d(TAG, "Linster interruped");
    207                 Log.d(TAG, "closing socket and stopping service");
    208                 safeClose(serverSocket);
    209                 safeClose(socket);
    210                 if (!bttraffic.isShuttingDown)
    211                     bttraffic.stopService();
    212             }
    213 
    214         }
    215 
    216         private void doListening(InputStream inputStream, OutputStream outputStream)
    217                 throws IOException {
    218             ByteBuffer byteBuffer = ByteBuffer.allocate(maxbuffersize);
    219 
    220             while (!Thread.interrupted()) {
    221                 readBytesIntoBuffer(inputStream, byteBuffer, 4);
    222                 byteBuffer.flip();
    223                 int length = byteBuffer.getInt();
    224                 if (Thread.interrupted())
    225                     break;
    226                 readBytesIntoBuffer(inputStream, byteBuffer, length);
    227 
    228                 if (sendAck)
    229                     outputStream.write(0x55);
    230             }
    231         }
    232 
    233         void readBytesIntoBuffer(InputStream inputStream, ByteBuffer byteBuffer, int numToRead)
    234                 throws IOException {
    235             byteBuffer.clear();
    236             while (true) {
    237                 int position = byteBuffer.position();
    238                 int remaining = numToRead - position;
    239                 if (remaining == 0) {
    240                     break;
    241                 }
    242                 int count = inputStream.read(byteBuffer.array(), position, remaining);
    243                 if (count < 0) {
    244                     throw new IOException("read the EOF");
    245                 }
    246                 byteBuffer.position(position + count);
    247             }
    248         }
    249     }
    250 
    251     public static class SenderRunnable implements Runnable {
    252         private final BTtraffic bttraffic;
    253         private final String remoteAddr;
    254         private final int pkgsize, period;
    255         private final int defaultpkgsize = 1024;
    256         private final int defaultperiod = 5000;
    257         private static ByteBuffer lengthBuffer = ByteBuffer.allocate(4);
    258 
    259         public SenderRunnable(BTtraffic bttraffic, String remoteAddr, Intent intent) {
    260             this.bttraffic = bttraffic;
    261             this.remoteAddr = remoteAddr;
    262             this.pkgsize = intent.getIntExtra("size", defaultpkgsize);
    263             this.period = intent.getIntExtra("period", defaultperiod);
    264         }
    265 
    266         @Override
    267         public void run() {
    268             BluetoothDevice device = null;
    269             try {
    270                 device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(remoteAddr);
    271             } catch (IllegalArgumentException e) {
    272                 Log.d(TAG, "Invalid BT MAC address!\n");
    273             }
    274             if (device == null) {
    275                 Log.d(TAG, "can't find matching device, stopping thread and service");
    276                 bttraffic.stopService();
    277                 return;
    278             }
    279 
    280             BluetoothSocket socket = null;
    281             try {
    282                 Log.d(TAG, "connecting to device with MAC addr: " + remoteAddr);
    283                 socket = device.createInsecureRfcommSocketToServiceRecord(SERVICE_UUID);
    284                 socket.connect();
    285                 Log.d(TAG, "connected, starting to send");
    286                 doSending(socket.getOutputStream());
    287                 Log.d(TAG, "send stopped, stopping service");
    288             } catch (Exception e) {
    289                 Log.d(TAG, "error while sending", e);
    290             } finally {
    291                 Log.d(TAG, "finishing, closing thread and service");
    292                 safeClose(socket);
    293                 if (!bttraffic.isShuttingDown)
    294                     bttraffic.stopService();
    295             }
    296         }
    297 
    298         private void doSending(OutputStream outputStream) throws IOException {
    299             Log.w(TAG, "doSending");
    300             try {
    301                 Random random = new Random(System.currentTimeMillis());
    302 
    303                 byte[] bytes = new byte[pkgsize];
    304                 random.nextBytes(bytes);
    305                 while (!Thread.interrupted()) {
    306                     writeBytes(outputStream, bytes.length);
    307                     outputStream.write(bytes, 0, bytes.length);
    308                     if (period < 0)
    309                         break;
    310                     if (period == 0)
    311                         continue;
    312 
    313                     SystemClock.sleep(period);
    314                 }
    315                 Log.d(TAG, "Sender interrupted");
    316             } catch (IOException e) {
    317                 Log.d(TAG, "doSending got error", e);
    318             }
    319         }
    320 
    321         private static void writeBytes(OutputStream outputStream, int value) throws IOException {
    322             lengthBuffer.putInt(value);
    323             lengthBuffer.flip();
    324             outputStream.write(lengthBuffer.array(), lengthBuffer.position(), lengthBuffer.limit());
    325         }
    326     }
    327 
    328 }
    329