2

Using Android's Bluetooth Chat sample app as my guide http://developer.android.com/resources/samples/BluetoothChat/index.html, I've tried to create my own bluetooth function for an app I am working on.

Last night I tested it with two Android phones and had some issues, but there were no force closes. It simply didn't connect my devices when I asked it to. I went in and added a few Log lines to make sure the program was following the proper course. When I reinstalled and launched the app on my phone today, I got a force close error when attempting to turn on Bluetooth, a problem I did not have at all last night. The only code I changed was the addition of 2-3 log commands. I checked logcat and got the following:

http://img708.imageshack.us/img708/143/logcatmysternpe.png

As you can see, the problem is caused by an NPE @ line 185. Here is my code for the BluetoothService class, I will repost the specific area of issue below it.

package com.tagapp.android;

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

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;

public class BluetoothService {

    private static final String TAG = "BluetoothService";
    private static final boolean D = true;
    private static final String NAME = "BluetoothTag";
    private static final UUID MY_UUID = 
        UUID.fromString("93845760-234e-11e0-ac64-0800200c9a66");

    private final BluetoothAdapter mAdapter;
    private final Handler mHandler;
    private AcceptThread mAcceptThread;
    private ConnectThread mConnectThread;
    private ConnectedThread mConnectedThread;
    private int mState;

    public static final int STATE_NONE = 0;
    public static final int STATE_LISTEN = 1;
    public static final int STATE_CONNECTING = 2;
    public static final int STATE_CONNECTED = 3;

    public BluetoothService(Context context, Handler handler) {
        mAdapter = BluetoothAdapter.getDefaultAdapter();
        mState = STATE_NONE;
        mHandler = handler;
    }

    private synchronized void setState(int state) {
        if (D) Log.d(TAG, "setState() " + mState + " -> " + state);
        mState = state;
        mHandler.obtainMessage(BluetoothTransfer.MESSAGE_STATE_CHANGE, state, -1).sendToTarget();
    }

    public synchronized int getState() {
        return mState;
    }

    public synchronized void start() {
        if (D) Log.d(TAG, "start");

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

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

        if(mAcceptThread == null) {
            mAcceptThread = new AcceptThread();
            mAcceptThread.start();
        }
        setState(STATE_LISTEN);
    }

    public synchronized void connect(BluetoothDevice device) {
        if (D) Log.d(TAG, "connect to: " + device);
        if(mState == STATE_CONNECTING) {
            if(mConnectThread != null) {
                mConnectThread.cancel();
                mConnectThread = null;
            }
        }
        if(mConnectedThread != null) {
            mConnectedThread.cancel();
            mConnectedThread = null;
        }
        mConnectThread = new ConnectThread(device);
        mConnectThread.start();
        setState(STATE_CONNECTING);
    }

    public synchronized void connected(BluetoothSocket socket, BluetoothDevice device) {
        if (D) Log.d(TAG, "connected");

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

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

        if(mAcceptThread != null) {
            mAcceptThread.cancel();
            mAcceptThread = null;
        }
        mConnectedThread = new ConnectedThread(socket);
        mConnectedThread.start();
        Message msg = mHandler.obtainMessage(BluetoothTransfer.MESSAGE_DEVICE_NAME);
        Bundle bundle = new Bundle();
        bundle.putString(BluetoothTransfer.DEVICE_NAME, device.getName());
        msg.setData(bundle);
        mHandler.sendMessage(msg);
        setState(STATE_CONNECTED);
    }

    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(mAcceptThread != null) {
            mAcceptThread.cancel();
            mAcceptThread = null;
        }
        setState(STATE_NONE);
    }

    public void write(byte[] out) {
        ConnectedThread ct;
        synchronized(this) {
            if(mState != STATE_CONNECTED) return;
            ct = mConnectedThread;
        }
        ct.write(out);
    }

    private void connectionFailed() {
        setState(STATE_LISTEN);
        Message msg = mHandler.obtainMessage(BluetoothTransfer.MESSAGE_TOAST);
        Bundle bundle = new Bundle();
        bundle.putString(BluetoothTransfer.TOAST, "Unable to connect device");
        msg.setData(bundle);
        mHandler.sendMessage(msg);
    }

    private void connectionLost() {
        setState(STATE_LISTEN);
        Message msg = mHandler.obtainMessage(BluetoothTransfer.MESSAGE_TOAST);
        Bundle bundle = new Bundle();
        bundle.putString(BluetoothTransfer.TOAST, "Device connection was lost");
        msg.setData(bundle);
        mHandler.sendMessage(msg);
    }

    private class AcceptThread extends Thread {
        private final BluetoothServerSocket mmServerSocket;

        public AcceptThread() {
            BluetoothServerSocket tmp = null;
            try {
                tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
            }
            catch (IOException e) {
                Log.e(TAG, "listen() failed", e);
            }
            mmServerSocket = tmp;
        }

        public void run() {
            if (D) Log.d(TAG, "BEGIN mAcceptThread" + this);
            setName("AcceptThread");
            BluetoothSocket socket = null;
            while (mState != STATE_CONNECTED) {
                try {
                    socket = mmServerSocket.accept();
                }
                catch (IOException e) {
                    Log.e(TAG, "accept() failed", e);
                    break;
                }
                if(socket != null) {
                    synchronized (BluetoothService.this) {
                        switch(mState) {
                        case STATE_LISTEN :
                        case STATE_CONNECTING :
                            connected(socket, socket.getRemoteDevice());
                            break;
                        case STATE_NONE :
                        case STATE_CONNECTED :
                            try {
                                socket.close();
                            }
                            catch (IOException e) {
                                Log.e(TAG, "Could not close unwanted socket", e);
                            }
                            break;
                        }
                    }
                }
            }
            if (D) Log.i(TAG, "END mAcceptThread");
        }

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

    private class ConnectThread extends Thread {
        private final BluetoothSocket mmSocket;
        private final BluetoothDevice mmDevice;

        public ConnectThread(BluetoothDevice device) {
            mmDevice = device;
            BluetoothSocket tmp = null;
            try {
                tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
            }
            catch (IOException e) {
                Log.e(TAG, "create() failed", e);
            }
            mmSocket = tmp;
        }

        public void run() {
            Log.i(TAG, "BEGIN mConnectThread");
            setName("ConnectThread");
            mAdapter.cancelDiscovery();
            try {
                mmSocket.connect();
            }
            catch (IOException e) {
                connectionFailed();
                try {
                    mmSocket.close();
                }
                catch (IOException e2) {
                    Log.e(TAG, "unable to close() socket during connection failure", e2);
                }
            BluetoothService.this.start();
            return;
            }
            synchronized (BluetoothService.this) {
                mConnectThread = null;
            }
            connected(mmSocket, mmDevice);
        }

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

    private class ConnectedThread extends Thread {

        private final BluetoothSocket mmSocket;
        private final InputStream mmInStream;
        private final OutputStream mmOutStream;

        public ConnectedThread(BluetoothSocket socket) {
            Log.d(TAG, "create ConnectedThread");
            mmSocket = socket;
            InputStream tmpIn = null;
            OutputStream tmpOut = null;
            try {
                tmpIn = socket.getInputStream();
                tmpOut = socket.getOutputStream();
            }
            catch (IOException e) {
                Log.e(TAG, "tempt sockets not created", e);
            }
            mmInStream = tmpIn;
            mmOutStream = tmpOut;
        }

        public void run() {
            Log.i(TAG, "BEGIN mConnectedThread");
            byte[] buffer = new byte[1024];
            int bytes;
            while (true) {
                try {
                    bytes = mmInStream.read(buffer);
                    mHandler.obtainMessage(BluetoothTransfer.CONTACT_RECEIVE, bytes, -1, buffer).sendToTarget();
                }
                catch (IOException e) {
                    Log.e(TAG, "disconnected", e);
                    connectionLost();
                    break;
                }
            }
        }

        public void write(byte[] buffer) {
            try {
                mmOutStream.write(buffer);
                mHandler.obtainMessage(BluetoothTransfer.CONTACT_SEND, -1, -1, buffer).sendToTarget();
            }
            catch (IOException e) {
                Log.e(TAG, "exception during write", e);
            }
        }

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

The lines which are causing the NPE (the AcceptThread):

private class AcceptThread extends Thread {
    private final BluetoothServerSocket mmServerSocket;

    public AcceptThread() {
        BluetoothServerSocket tmp = null;
        try {
            tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
        }
        catch (IOException e) {
            Log.e(TAG, "listen() failed", e);
        }
        mmServerSocket = tmp;
    }

    public void run() {
        if (D) Log.d(TAG, "BEGIN mAcceptThread" + this);
        setName("AcceptThread");
        BluetoothSocket socket = null;
        while (mState != STATE_CONNECTED) {
            try {
                socket = mmServerSocket.accept();//THIS LINE CAUSES NPE
            }
            catch (IOException e) {
                Log.e(TAG, "accept() failed", e);
                break;
            }

The object socket should by initialized @ null, but it is supposed to try socket = mmServerSocket.accept();

This is directly from Google's sample app provided on the Android dev website. I have two concerns: 1, why isn't this working, and 2, why did it work just fine just hours ago?

Thanks for your help.

Austyn Mahoney
  • 11,398
  • 8
  • 64
  • 85
JMRboosties
  • 15,500
  • 20
  • 76
  • 116
  • Just as a note, the actual exception appears to be an `IOException` on the `listen()`, though it would appear that your app should be handling that. Is the bluetooth turned off? – Daniel DiPaolo Feb 02 '11 at 01:34
  • Yes, bluetooth is off but the app is supposed to turn it on. Last night when I was running it from this same phone it gave me a prompt to turn bluetooth on and did so with no errors. – JMRboosties Feb 02 '11 at 02:11
  • I agree with @Daniel. The issue is likely that you are having a blue tooth service issue. The `accept()` call has thrown an `IOException` and, from the documentation, the reasons for such exceptions are 'on error, for example Bluetooth not available, or insufficient permissions, or channel in use.' Is it possible that another instance of your app is running or that some other service is bound to it? – Nick Campion Feb 02 '11 at 02:19
  • I don't think so. I've tried restarting my phone, using ATK to kill all programs, etc. When I run the application with bluetooth started up prior, it runs OK. Also, when I force close the app on the error occuring (Bluetooth will have been started at this point) it doesn't cause (at least noticeable) problems either. It's on trying to start bluetooth when it is off where this problem occurs. The proper permissions should be there. Any ideas? – JMRboosties Feb 02 '11 at 02:25
  • Thank you Austyn for editing my question to display how I wanted. – JMRboosties Feb 02 '11 at 03:26

1 Answers1

1

Regarding "strange NPE" - it's not strange at all. The reason you get NPE at this line

socket = mmServerSocket.accept(); //THIS LINE CAUSES NPE

is pretty obvious - variable mmServerSocket equals NULL, and this is the only possible reason to get NPE at this line of code. The reason why mmServerSocket equals NULL is simple too - look at a constructor of your class:

public AcceptThread() {
    BluetoothServerSocket tmp = null;
    try {
        tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
    }
    catch (IOException e) {
        Log.e(TAG, "listen() failed", e);
    }
    mmServerSocket = tmp;
}

You are calling method listenUsingRfcommWithServiceRecord which throws IOException, you catching this exception, as we can clearly see in logcat. Next, tmp variable stays initialized as NULL, same as mmServerSocket, which leads as to NPE mentioned above.

The reason why listenUsingRfcommWithServiceRecord is throwing IOException - because bluetooth is turned off, as you mention in your question. Android documentation says nothing about your assumption that this method should automatically turn on bluetooth if it's turned off. I think you should manually check if bluetooth is turned off and turn it on manually, before calling listenUsingRfcommWithServiceRecord.

You can find how to check is bluetooth turned on/off and turn it on here and here

Community
  • 1
  • 1
HitOdessit
  • 7,198
  • 4
  • 36
  • 59
  • This explanation worked right for me. The issue (i.e. NPE error)got resolved after enabling the BTadapter. – SunGa Aug 23 '14 at 13:22