Android Open Source - bluedroid-mp Bluetooth Chat Service






From Project

Back to project page bluedroid-mp.

License

The source code is released under:

Apache License

If you think the Android project bluedroid-mp listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package uk.ac.gcu.bluedroid;
//from  w w  w. j a  va2  s . co  m
/*
 * Copyright (C) 2009 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.UUID;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import uk.ac.gcu.bluedroid.game.GameState;
import uk.ac.gcu.bluedroid.game.TurnInfo;
import uk.ac.gcu.bluedroid.util.Util;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;

/**
 * This class does all the work for setting up and managing Bluetooth
 * connections with other devices. It has a thread that listens for incoming
 * connections, a thread for connecting with a device, and a thread for
 * performing data transmissions when connected.
 */
public class BluetoothChatService {
  // Debugging
  private static final String TAG = "BluetoothChatService";
  private static final boolean D = true;

  // Name for the SDP record when creating server socket
  private static final String NAME_SECURE = "BluetoothChatSecure";
  private static final String NAME_INSECURE = "BluetoothChatInsecure";

  // Unique UUID for this application
  private static final UUID MY_UUID_SECURE = UUID
      .fromString("fa87c0d0-afac-11de-8a39-0800200c9a66");
  private static final UUID MY_UUID_INSECURE = UUID
      .fromString("8ce255c0-200a-11e0-ac64-0800200c9a66");

  // Member fields
  private final BluetoothAdapter mAdapter;
  private final Handler mHandler;
  private AcceptThread mSecureAcceptThread;
  private AcceptThread mInsecureAcceptThread;
  private ConnectThread mConnectThread;
  private ConnectedThread mConnectedThread;
  private int mState;
  
  // Time to wait for acknowledge (in milliseconds)
  private static final int ACK_WAIT = 3000;

  // Acknowledge Handler
  Handler ackHandler;
  ArrayList<String> tags;
  
  // JSON parser
  Gson gson;

  // Constants that indicate the current connection state
  public static final int STATE_NONE = 0; // we're doing nothing
  public static final int STATE_LISTEN = 1; // now listening for incoming
                        // connections
  public static final int STATE_CONNECTING = 2; // now initiating an outgoing
                          // connection
  public static final int STATE_CONNECTED = 3; // now connected to a remote
                          // device

  /**
   * Constructor. Prepares a new MainActivity session.
   * 
   * @param context
   *            The UI Activity Context
   * @param handler
   *            A Handler to send messages back to the UI Activity
   */
  public BluetoothChatService(Context context, Handler handler) {
    GsonBuilder builder = new GsonBuilder();
    gson = builder.create();
    ackHandler = new Handler();
    
    mAdapter = BluetoothAdapter.getDefaultAdapter();
    mState = STATE_NONE;
    mHandler = handler;
    tags = new ArrayList<String>();
  }

  /**
   * Set the current state of the chat connection
   * 
   * @param state
   *            An integer defining the current connection state
   */
  private synchronized void setState(int state) {
    if (D)
      Log.d(TAG, "setState() " + mState + " -> " + state);
    mState = state;

    // Give the new state to the Handler so the UI Activity can update
    mHandler.obtainMessage(MainActivity.MESSAGE_STATE_CHANGE, state, -1)
        .sendToTarget();
  }

  /**
   * Return the current connection state.
   */
  public synchronized int getState() {
    return mState;
  }

  /**
   * Start the chat service. Specifically start AcceptThread to begin a
   * session in listening (server) mode. Called by the Activity onResume()
   */
  public synchronized void start() {
    if (D)
      Log.d(TAG, "start");

    // Cancel any thread attempting to make a connection
    if (mConnectThread != null) {
      mConnectThread.cancel();
      mConnectThread = null;
    }

    // Cancel any thread currently running a connection
    if (mConnectedThread != null) {
      mConnectedThread.cancel();
      mConnectedThread = null;
    }

    setState(STATE_LISTEN);

    // Start the thread to listen on a BluetoothServerSocket
    if (mSecureAcceptThread == null) {
      mSecureAcceptThread = new AcceptThread(true);
      mSecureAcceptThread.start();
    }
    if (mInsecureAcceptThread == null) {
      mInsecureAcceptThread = new AcceptThread(false);
      mInsecureAcceptThread.start();
    }
  }

  /**
   * Start the ConnectThread to initiate a connection to a remote device.
   * 
   * @param device
   *            The BluetoothDevice to connect
   * @param secure
   *            Socket Security type - Secure (true) , Insecure (false)
   */
  public synchronized void connect(BluetoothDevice device, boolean secure) {
    if (D)
      Log.d(TAG, "connect to: " + device);

    // Cancel any thread attempting to make a connection
    if (mState == STATE_CONNECTING) {
      if (mConnectThread != null) {
        mConnectThread.cancel();
        mConnectThread = null;
      }
    }

    // Cancel any thread currently running a connection
    if (mConnectedThread != null) {
      mConnectedThread.cancel();
      mConnectedThread = null;
    }

    // Start the thread to connect with the given device
    mConnectThread = new ConnectThread(device, secure);
    mConnectThread.start();
    setState(STATE_CONNECTING);
  }

  /**
   * Start the ConnectedThread to begin managing a Bluetooth connection
   * 
   * @param socket
   *            The BluetoothSocket on which the connection was made
   * @param device
   *            The BluetoothDevice that has been connected
   */
  public synchronized void connected(BluetoothSocket socket,
      BluetoothDevice device, final String socketType) {
    if (D)
      Log.d(TAG, "connected, Socket Type:" + socketType);

    // Cancel the thread that completed the connection
    if (mConnectThread != null) {
      mConnectThread.cancel();
      mConnectThread = null;
    }

    // Cancel any thread currently running a connection
    if (mConnectedThread != null) {
      mConnectedThread.cancel();
      mConnectedThread = null;
    }

    // Cancel the accept thread because we only want to connect to one
    // device
    if (mSecureAcceptThread != null) {
      mSecureAcceptThread.cancel();
      mSecureAcceptThread = null;
    }
    if (mInsecureAcceptThread != null) {
      mInsecureAcceptThread.cancel();
      mInsecureAcceptThread = null;
    }

    // Start the thread to manage the connection and perform transmissions
    mConnectedThread = new ConnectedThread(socket, socketType);
    mConnectedThread.start();

    // Send the name of the connected device back to the UI Activity
    Message msg = mHandler.obtainMessage(MainActivity.MESSAGE_DEVICE_NAME);
    Bundle bundle = new Bundle();
    bundle.putString(MainActivity.DEVICE_NAME, device.getName());
    msg.setData(bundle);
    mHandler.sendMessage(msg);

    setState(STATE_CONNECTED);
  }

  /**
   * Stop all threads
   */
  public synchronized void stop() {
    if (D)
      Log.d(TAG, "stop");

    if (mConnectThread != null) {
      mConnectThread.cancel();
      mConnectThread = null;
    }

    if (mConnectedThread != null) {
      mConnectedThread.cancel();
      mConnectedThread = null;
    }

    if (mSecureAcceptThread != null) {
      mSecureAcceptThread.cancel();
      mSecureAcceptThread = null;
    }

    if (mInsecureAcceptThread != null) {
      mInsecureAcceptThread.cancel();
      mInsecureAcceptThread = null;
    }
    setState(STATE_NONE);
  }

  public void send(String _class, Object obj) {
    Wrapper wrapper = new Wrapper(_class, Wrapper.MESSAGE, gson.toJson(obj));

    write(wrapper);
  }

  /**
   * Write to the ConnectedThread in an unsynchronized manner
   * 
   * @param out
   *            The bytes to write
   * @see ConnectedThread#write(Wrapper)
   */
  private void write(Wrapper out) {
    // Create temporary object
    ConnectedThread r;
    // Synchronize a copy of the ConnectedThread
    synchronized (this) {
      if (mState != STATE_CONNECTED)
        return;
      r = mConnectedThread;
    }
    // Perform the write unsynchronized
    r.write(out);
  }
    
  /**
   * Indicate that the connection attempt failed and notify the UI Activity.
   */
  private void connectionFailed() {
    // Send a failure message back to the Activity
    Message msg = mHandler.obtainMessage(MainActivity.MESSAGE_TOAST);
    Bundle bundle = new Bundle();
    bundle.putString(MainActivity.TOAST, "Unable to connect device");
    msg.setData(bundle);
    mHandler.sendMessage(msg);

    // Start the service over to restart listening mode
    BluetoothChatService.this.start();
  }

  /**
   * Indicate that the connection was lost and notify the UI Activity.
   */
  private void connectionLost() {
    // Send a failure message back to the Activity
    Message msg = mHandler.obtainMessage(MainActivity.MESSAGE_TOAST);
    Bundle bundle = new Bundle();
    bundle.putString(MainActivity.TOAST, "Device connection was lost");
    msg.setData(bundle);
    mHandler.sendMessage(msg);

    // Start the service over to restart listening mode
    BluetoothChatService.this.start();
  }

  /**
   * This thread runs while listening for incoming connections. It behaves
   * like a server-side client. It runs until a connection is accepted (or
   * until cancelled).
   */
  private class AcceptThread extends Thread {
    // The local server socket
    private final BluetoothServerSocket mmServerSocket;
    private String mSocketType;

    public AcceptThread(boolean secure) {
      BluetoothServerSocket tmp = null;
      mSocketType = secure ? "Secure" : "Insecure";

      // Create a new listening server socket
      try {
        if (secure) {
          tmp = mAdapter.listenUsingRfcommWithServiceRecord(
              NAME_SECURE, MY_UUID_SECURE);
        } else {
          // listenUsingInsecureRfcommWithServiceRecord(String name,
          // UUID uuid)

          // tmp =
          // mAdapter.listenUsingInsecureRfcommWithServiceRecord(NAME_INSECURE,
          // MY_UUID_INSECURE);
          tmp = mAdapter.listenUsingRfcommWithServiceRecord(
              NAME_INSECURE, MY_UUID_INSECURE);
        }
      } catch (IOException e) {
        Log.e(TAG, "Socket Type: " + mSocketType + "listen() failed", e);
      }
      mmServerSocket = tmp;
    }

    public void run() {
      if (D)
        Log.d(TAG, "Socket Type: " + mSocketType
            + "BEGIN mAcceptThread" + this);
      setName("AcceptThread" + mSocketType);

      BluetoothSocket socket = null;

      // Listen to the server socket if we're not connected
      while (mState != STATE_CONNECTED) {
        try {
          // This is a blocking call and will only return on a
          // successful connection or an exception
          socket = mmServerSocket.accept();
        } catch (IOException e) {
          Log.e(TAG, "Socket Type: " + mSocketType
              + "accept() failed", e);
          break;
        }

        // If a connection was accepted
        if (socket != null) {
          synchronized (BluetoothChatService.this) {
            switch (mState) {
            case STATE_LISTEN:
            case STATE_CONNECTING:
              // Situation normal. Start the connected thread.
              connected(socket, socket.getRemoteDevice(),
                  mSocketType);
              break;
            case STATE_NONE:
            case STATE_CONNECTED:
              // Either not ready or already connected. Terminate
              // new socket.
              try {
                socket.close();
              } catch (IOException e) {
                Log.e(TAG, "Could not close unwanted socket", e);
              }
              break;
            }
          }
        }
      }
      if (D)
        Log.i(TAG, "END mAcceptThread, socket Type: " + mSocketType);

    }

    public void cancel() {
      if (D)
        Log.d(TAG, "Socket Type" + mSocketType + "cancel " + this);
      try {
        mmServerSocket.close();
      } catch (IOException e) {
        Log.e(TAG, "Socket Type" + mSocketType
            + "close() of server failed", e);
      }
    }
  }

  /**
   * This thread runs while attempting to make an outgoing connection with a
   * device. It runs straight through; the connection either succeeds or
   * fails.
   */
  private class ConnectThread extends Thread {
    private final BluetoothSocket mmSocket;
    private final BluetoothDevice mmDevice;
    private String mSocketType;

    public ConnectThread(BluetoothDevice device, boolean secure) {
      mmDevice = device;
      BluetoothSocket tmp = null;
      mSocketType = secure ? "Secure" : "Insecure";

      // Get a BluetoothSocket for a connection with the
      // given BluetoothDevice
      try {
        if (secure) {
          tmp = device
              .createRfcommSocketToServiceRecord(MY_UUID_SECURE);
        } else {
          // tmp =
          // device.createInsecureRfcommSocketToServiceRecord(MY_UUID_INSECURE);
          tmp = device
              .createRfcommSocketToServiceRecord(MY_UUID_INSECURE);
        }
      } catch (IOException e) {
        Log.e(TAG, "Socket Type: " + mSocketType + "create() failed", e);
      }
      mmSocket = tmp;
    }

    public void run() {
      Log.i(TAG, "BEGIN mConnectThread SocketType:" + mSocketType);
      setName("ConnectThread" + mSocketType);

      // Always cancel discovery because it will slow down a connection
      mAdapter.cancelDiscovery();

      // Make a connection to the BluetoothSocket
      try {
        // This is a blocking call and will only return on a
        // successful connection or an exception
        mmSocket.connect();
      } catch (IOException e) {
        // Close the socket
        try {
          mmSocket.close();
        } catch (IOException e2) {
          Log.e(TAG, "unable to close() " + mSocketType
              + " socket during connection failure", e2);
        }
        connectionFailed();
        return;
      }

      // Reset the ConnectThread because we're done
      synchronized (BluetoothChatService.this) {
        mConnectThread = null;
      }

      // Start the connected thread
      connected(mmSocket, mmDevice, mSocketType);
    }

    public void cancel() {
      try {
        mmSocket.close();
      } catch (IOException e) {
        Log.e(TAG, "close() of connect " + mSocketType
            + " socket failed", e);
      }
    }
  }

  /**
   * This thread runs during a connection with a remote device. It handles all
   * incoming and outgoing transmissions.
   */
  private class ConnectedThread extends Thread {
    private final BluetoothSocket mmSocket;
    private final InputStream mmInStream;
    private final OutputStream mmOutStream;

    public ConnectedThread(BluetoothSocket socket, String socketType) {
      Log.d(TAG, "create ConnectedThread: " + socketType);
      mmSocket = socket;
      InputStream tmpIn = null;
      OutputStream tmpOut = null;

      // Get the BluetoothSocket input and output streams
      try {
        tmpIn = socket.getInputStream();
        tmpOut = socket.getOutputStream();
      } catch (IOException e) {
        Log.e(TAG, "temp sockets not created", e);
      }

      mmInStream = tmpIn;
      mmOutStream = tmpOut;
    }
    
    public boolean isJSONValid(String test)
    {
        try 
        {
            new JSONObject(test);
        } 
        catch(JSONException ex) 
        {
            try 
            {
                new JSONArray(test);
            } 
            catch(JSONException ex2) 
            {
                return false;
            }
        }
        return true;
    }

    public void run() {
            Log.i(TAG, "BEGIN mConnectedThread");
            byte[] buffer = new byte[20000];
            int bytes;

            // Keep listening to the InputStream while connected
            while (true) {
                try {
                    // Read from the InputStream
                    bytes = mmInStream.read(buffer);
                    
                    String json = new String(buffer, 0, bytes);
                    while(!isJSONValid(json)) {
                      bytes = mmInStream.read(buffer);
                      json = json.concat(new String(buffer, 0, bytes));
                    }
                    
                    Wrapper wrapper = gson.fromJson(json, Wrapper.class);

                    switch(wrapper.type) {
                    case Wrapper.ACK:
                      Log.d(TAG, "Acknowledge for message " + wrapper.message + " received.");

                      // Do not send the message again
                      tags.remove(wrapper.message);
                      break;
                    case Wrapper.MESSAGE:
                      Object obj = null;
                      
                      if(wrapper._class.equals("string"))
                        obj = gson.fromJson(wrapper.message, String.class);
                      else if(wrapper._class.equals("state"))
                        obj = gson.fromJson(wrapper.message, GameState.class);
                      else if(wrapper._class.equals("turn_info"))
                        obj = gson.fromJson(wrapper.message, TurnInfo.class);
                      
                        mHandler.obtainMessage(MainActivity.MESSAGE_RECEIVED, obj).sendToTarget();
                       
                        // Send ACK
                        if(wrapper.hash == null || wrapper.hash.equals(Util.hash(wrapper.message)))
                          write(new Wrapper("string", Wrapper.ACK, String.valueOf(wrapper.time)));
                        
                      break;
                    }
                } catch (IOException e) {
                    Log.e(TAG, "disconnected", e);
                    connectionLost();
                    // Start the service over to restart listening mode
                    BluetoothChatService.this.start();
                    break;
                }
            }
        }

    /**
     * Write to the connected OutStream.
     * 
     * @param buffer
     *            The bytes to write
     */
    public void write(final Wrapper buffer) {
      Runnable writemsg = new Runnable() {
        @Override
        public void run() {
          try {
            if(!tags.contains(String.valueOf(buffer.time)))
              return;
            
            mmOutStream.write(gson.toJson(buffer).getBytes());
            
            if (buffer.type != Wrapper.ACK) {
              Log.d(TAG, "Sending message " + String.valueOf(buffer.time));

              mHandler.obtainMessage(MainActivity.MESSAGE_SENT,
                  gson.fromJson(buffer.message, Object.class)).sendToTarget();
              
              // Schedule to send message again in ACK_WAIT milliseconds
              ackHandler.postDelayed(this, ACK_WAIT);
            } else
              Log.d(TAG, "Sending ack for " + buffer.message);
          } catch (IOException e) {
            Log.e(TAG, "Exception during write", e);
          }
        }
      };

      tags.add(String.valueOf(buffer.time));
      ackHandler.post(writemsg);
    }

    public void cancel() {
      try {
        mmSocket.close();
      } catch (IOException e) {
        Log.e(TAG, "close() of connect socket failed", e);
      }
    }
  }
}




Java Source Code List

uk.ac.gcu.bluedroid.BluetoothChatService.java
uk.ac.gcu.bluedroid.DeviceListActivity.java
uk.ac.gcu.bluedroid.MainActivity.java
uk.ac.gcu.bluedroid.Wrapper.java
uk.ac.gcu.bluedroid.game.GameState.java
uk.ac.gcu.bluedroid.game.Map.java
uk.ac.gcu.bluedroid.game.Maps.java
uk.ac.gcu.bluedroid.game.Player.java
uk.ac.gcu.bluedroid.game.TurnInfo.java
uk.ac.gcu.bluedroid.resources.Camp.java
uk.ac.gcu.bluedroid.resources.Crop.java
uk.ac.gcu.bluedroid.resources.Mine.java
uk.ac.gcu.bluedroid.resources.Resource.java
uk.ac.gcu.bluedroid.units.Archer.java
uk.ac.gcu.bluedroid.units.Paladin.java
uk.ac.gcu.bluedroid.units.Soldier.java
uk.ac.gcu.bluedroid.units.Unit.java
uk.ac.gcu.bluedroid.util.CustomImageVIew.java
uk.ac.gcu.bluedroid.util.MyTextView.java
uk.ac.gcu.bluedroid.util.Node.java
uk.ac.gcu.bluedroid.util.Pathfinder.java
uk.ac.gcu.bluedroid.util.Position.java
uk.ac.gcu.bluedroid.util.Util.java