BLE send/receive;Attempt to read from field 'android.bluetooth.BluetoothGattCallback BluetoothLeService.mGattCallback' on a null object reference

809 views Asked by At

I do not know much about android. I am trying to send data over BLE.my code is as follows->. I got it from internet https://developer.android.com/guide/topics/connectivity/bluetooth-le#read

how to properly call this method-> mBluetoothService.mBluetoothGatt = device.connectGatt(cntxt, false, mBluetoothService.mGattCallback);There is crash on this line.

    public class BluetoothLeService extends Service {
    private final static String TAG = BluetoothLeService.class.getSimpleName();

    public BluetoothManager mBluetoothManager;
    public BluetoothAdapter mBluetoothAdapter;
    public String mBluetoothDeviceAddress;
    public BluetoothGatt mBluetoothGatt;
    private int mConnectionState = STATE_DISCONNECTED;
    private final IBinder mBinder = new LocalBinder();

    private static final int STATE_DISCONNECTED = 0;
    private static final int STATE_CONNECTING = 1;
    private static final int STATE_CONNECTED = 2;

    public final static String ACTION_GATT_CONNECTED =
            "com.example.bluetooth.le.ACTION_GATT_CONNECTED";
    public final static String ACTION_GATT_DISCONNECTED =
            "com.example.bluetooth.le.ACTION_GATT_DISCONNECTED";
    public final static String ACTION_GATT_SERVICES_DISCOVERED =
            "com.example.bluetooth.le.ACTION_GATT_SERVICES_DISCOVERED";
    public final static String ACTION_DATA_AVAILABLE =
            "com.example.bluetooth.le.ACTION_DATA_AVAILABLE";
    public final static String EXTRA_DATA =
            "com.example.bluetooth.le.EXTRA_DATA";

    public final static UUID UUID_HEART_RATE_MEASUREMENT =
            UUID.fromString(SampleGattAttributes.HEART_RATE_MEASUREMENT);

    // Various callback methods defined by the BLE API.
    public final BluetoothGattCallback mGattCallback =
            new BluetoothGattCallback() {
                @Override
                public void onConnectionStateChange(BluetoothGatt gatt, int status,
                                                    int newState) {
                    String intentAction;
                    if (newState == BluetoothProfile.STATE_CONNECTED) {
                        intentAction = ACTION_GATT_CONNECTED;
                        mConnectionState = STATE_CONNECTED;
                        broadcastUpdate(intentAction);
                        Log.i(TAG, "Connected to GATT server.");
                        Log.i(TAG, "Attempting to start service discovery:" +
                                mBluetoothGatt.discoverServices());

                    } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                        intentAction = ACTION_GATT_DISCONNECTED;
                        mConnectionState = STATE_DISCONNECTED;
                        Log.i(TAG, "Disconnected from GATT server.");
                        broadcastUpdate(intentAction);
                    }
                }

                @Override
                // New services discovered
                public void onServicesDiscovered(BluetoothGatt gatt, int status) {
                    if (status == BluetoothGatt.GATT_SUCCESS) {
                        //broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
                        for (BluetoothGattService gattService : gatt.getServices()) {
                            Log.i(TAG, "onServicesDiscovered: ---------------------");
                            Log.i(TAG, "onServicesDiscovered: service=" + gattService.getUuid());
                            for (BluetoothGattCharacteristic characteristic : gattService.getCharacteristics()) {
                                Log.i(TAG, "onServicesDiscovered: characteristic=" + characteristic.getUuid());

                                if (characteristic.getUuid().toString().equals("0000ffe9-0000-1000-8000-00805f9b34fb")) {

                                    Log.w(TAG, "onServicesDiscovered: found LED");

                                    String originalString = "560D0F0600F0AA";

                                    byte[] b = hexStringToByteArray(originalString);

                                    characteristic.setValue(b); // call this BEFORE(!) you 'write' any stuff to the server
                                    mBluetoothGatt.writeCharacteristic(characteristic);

                                    Log.i(TAG, "onServicesDiscovered: , write bytes?! " + byteToHexStr(b));
                                }
                            }
                        }

                        broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);

                    } else {
                        Log.w(TAG, "onServicesDiscovered received: " + status);
                    }
                }

                @Override
                // Result of a characteristic read operation
                public void onCharacteristicRead(BluetoothGatt gatt,
                                                 BluetoothGattCharacteristic characteristic,
                                                 int status) {
                    if (status == BluetoothGatt.GATT_SUCCESS) {
                        byte[] data = characteristic.getValue();
                        System.out.println("reading");
                        System.out.println(new String(data));
                        broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
                    }
                }
                //...
            };
    private void broadcastUpdate(final String action) {
        final Intent intent = new Intent(action);
        sendBroadcast(intent);
    }

    public static byte[] hexStringToByteArray(String s) {
        int len = s.length();
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
                    + Character.digit(s.charAt(i + 1), 16));
        }
        return data;
    }

    public static String byteToHexStr(byte[] bytes) {
        StringBuilder builder = new StringBuilder();
        for (byte b: bytes) {
            builder.append(String.format("%02x", b));
        }
        return builder.toString();
    }


    private void broadcastUpdate(final String action,
                                 final BluetoothGattCharacteristic characteristic) {
        final Intent intent = new Intent(action);

        // This is special handling for the Heart Rate Measurement profile. Data
        // parsing is carried out as per profile specifications.
        if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {
            int flag = characteristic.getProperties();
            int format = -1;
            if ((flag & 0x01) != 0) {
                format = BluetoothGattCharacteristic.FORMAT_UINT16;
                Log.d(TAG, "Heart rate format UINT16.");
            } else {
                format = BluetoothGattCharacteristic.FORMAT_UINT8;
                Log.d(TAG, "Heart rate format UINT8.");
            }
            final int heartRate = characteristic.getIntValue(format, 1);
            Log.d(TAG, String.format("Received heart rate: %d", heartRate));
            intent.putExtra(EXTRA_DATA, String.valueOf(heartRate));
        } else {
            // For all other profiles, writes the data formatted in HEX.
            final byte[] data = characteristic.getValue();
            if (data != null && data.length > 0) {
                final StringBuilder stringBuilder = new StringBuilder(data.length);
                for(byte byteChar : data)
                    stringBuilder.append(String.format("%02X ", byteChar));
                intent.putExtra(EXTRA_DATA, new String(data) + "\n" +
                        stringBuilder.toString());
            }
        }
        sendBroadcast(intent);
    }
    public class LocalBinder extends Binder {
        BluetoothLeService getService() {
            return BluetoothLeService.this;
        }
    }
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;/*return null;*/
    }
//...
}

I am calling this class methods from outside like this->

    public class DeviceScanActivity extends AppCompatActivity/*ListActivity*/ {
        //private LeDeviceListAdapter mLeDeviceListAdapter;
        public BluetoothAdapter mBluetoothAdapter;
        public /*final*/ BluetoothManager mBluetoothManager;
                /*=(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);;*/
        private BluetoothLeService mBluetoothService;//= new BluetoothLeService();
        public Context cntxt;
        ArrayList<ViewHolder> arrayOfUsers2 = new ArrayList<ViewHolder>();
        private boolean mScanning;
        private boolean mBounded;
        private Handler mHandler;
        ArrayList<String> mylist = new ArrayList<String>();
        private static final int REQUEST_ENABLE_BT = 1;
        // Stops scanning after 10 seconds.
        private static final long SCAN_PERIOD = 50000;
        private static final int PERMISSION_REQUEST_COARSE_LOCATION = 456;

        UsersAdapter adapter;
        @Override
        public void onCreate(Bundle savedInstanceState) {

            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
            //getActionBar().setTitle("abc");

            mHandler = new Handler();
            //this.requestPermissions(rqPerm,req);

            requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, PERMISSION_REQUEST_COARSE_LOCATION);
            // Use this check to determine whether BLE is supported on the device.  Then you can
            // selectively disable BLE-related features.
            if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
                Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
                finish();
            }
            // Initializes a Bluetooth adapter.  For API level 18 and above, get a reference to
            // BluetoothAdapter through BluetoothManager.
            /*final BluetoothManager*/ mBluetoothManager =
                    (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
            mBluetoothAdapter = mBluetoothManager.getAdapter();
            // Checks if Bluetooth is supported on the device.
            if (mBluetoothAdapter == null ) {
                Toast.makeText(this, R.string.error_bluetooth_not_supported, Toast.LENGTH_SHORT).show();
                finish();
                return;
            }
            if( !mBluetoothAdapter.isEnabled())
            {
                Intent enableBluetooth = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
                startActivityForResult(getIntent(), 1);
            }
            // Construct the data source

            ArrayList<ViewHolder> arrayOfUsers = new ArrayList<ViewHolder>();

           // Create the adapter to convert the array to views

             adapter = new UsersAdapter(this, arrayOfUsers);
             cntxt=this.getApplicationContext();
            Intent i = new Intent(this, BluetoothLeService.class);
            bindService(this.getIntent(), mConnection, BIND_AUTO_CREATE);; //if checked, start service
            ListView listView = (ListView) findViewById(R.id.mobile_list);
            listView.setAdapter(adapter);
            listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
                @Override
                public void onItemClick(AdapterView<?> parent, View view, int position,
                                        long id) {
                    ViewHolder entry= (ViewHolder) parent.getAdapter().getItem(position);
                    String address = entry.deviceAddress;
                    final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
                    Toast.makeText(cntxt, address, Toast.LENGTH_SHORT).show();
//mBluetoothService.mBluetoothDeviceAddress=address;
                    //mBluetoothService.mBluetoothManager=mBluetoothManager;
                    mBluetoothService.mBluetoothAdapter = mBluetoothAdapter;
                    mBluetoothService.mBluetoothGatt = device.connectGatt(cntxt, false, mBluetoothService.mGattCallback);

                    //Toast.makeText(this, R.string.error_bluetooth_not_supported, Toast.LENGTH_SHORT).show();
                }});
            ViewHolder newUser2 = new ViewHolder("adtv2","vvg2");
             adapter.add(newUser2);

        }
        ServiceConnection mConnection = new ServiceConnection() {
            @Override
            public void onServiceDisconnected(ComponentName name) {
                //Toast.makeText(Client.this, "Service is disconnected", 1000).show();
                mBounded = false;
                mBluetoothService = null;
            }
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                //Toast.makeText(Client.this, "Service is connected", 1000).show();
                mBounded = true;
                BluetoothLeService.LocalBinder mLocalBinder = (BluetoothLeService.LocalBinder)service;
                mBluetoothService = mLocalBinder.getService();
            }
        };


        private void scanLeDevice(final boolean enable) {
            if (enable) {
                // Stops scanning after a pre-defined scan period.
                ViewHolder newUser2 = new ViewHolder("adtv2","vvg2");
                adapter.add(newUser2);
                mHandler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        mScanning = false;
                        mBluetoothAdapter.stopLeScan(mLeScanCallback);



                        Iterator<ViewHolder> it=arrayOfUsers2.iterator();
                        while(it.hasNext()) {
                            ViewHolder currentX = it.next();
                            adapter.add(currentX);
                        }
                            // Do something with the value

                    }
                }, SCAN_PERIOD);

                mScanning = true;
                mBluetoothAdapter.startLeScan(mLeScanCallback);
            } else {
                mScanning = false;
                mBluetoothAdapter.stopLeScan(mLeScanCallback);
            }
            //invalidateOptionsMenu();
        }
        @Override
        public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
            switch (requestCode) {
                case PERMISSION_REQUEST_COARSE_LOCATION: {
                    if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                        // Permission granted, yay! Start the Bluetooth device scan.
                        scanLeDevice(true);
                    } else {
                        // Alert the user that this application requires the location permission to perform the scan.
                    }
                }
            }
        }


        // Device scan callback.
        private BluetoothAdapter.LeScanCallback mLeScanCallback =
                new BluetoothAdapter.LeScanCallback() {

                    @Override
                    public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                //mLeDeviceListAdapter.addDevice(device);
                                //mLeDeviceListAdapter.notifyDataSetChanged();

                                //ViewHolder newUser = new ViewHolder("Nathan", "San Diego");
                                String deviceName=null, deviceAddress=null;
                                if(device!=null)
                                   deviceName= device.getName();
                                if (!(deviceName != null && deviceName.length() > 0))
                                    deviceName = "unknown device";
                                if(device!=null)
                                    deviceAddress= device.getAddress();
                                ViewHolder newUser = new ViewHolder(deviceName, deviceAddress);
                                ViewHolder newUser2 = new ViewHolder("adtv","vvg");
                                if(!arrayOfUsers2.contains(newUser))
                                    arrayOfUsers2.add(newUser);
                                //adapter.add(newUser);
                            }
                        });
                    }
                };
        public class UsersAdapter extends ArrayAdapter<ViewHolder> {


            public UsersAdapter(Context context, ArrayList<ViewHolder> users) {

                super(context, 0, users);

            }



            @Override

            public View getView(int position, View convertView, ViewGroup parent) {

                // Get the data item for this position

                ViewHolder user = getItem(position);


                // Check if an existing view is being reused, otherwise inflate the view

                if (convertView == null) {

                    convertView = LayoutInflater.from(getContext()).inflate(R.layout.bt_details, parent, false);

                }

                // Lookup view for data population

                TextView tvName = (TextView) convertView.findViewById(R.id.DeviceName);

                TextView tvHome = (TextView) convertView.findViewById(R.id.DeviceAddress);

                // Populate the data into the template view using the data object

                tvName.setText(user.deviceName);

                tvHome.setText(user.deviceAddress);

                // Return the completed view to render on screen

                return convertView;

            }

        }
        public class ViewHolder {
            String deviceName;
            String deviceAddress;

            public ViewHolder(String device, String __address) {
                this.deviceName =device;
                this.deviceAddress= __address;
            }
            @Override
            public boolean equals(Object o) {
                if (this == o) return true;
                if (o == null || getClass() != o.getClass()) return false;
                ViewHolder a = (ViewHolder) o;
                return Objects.equals(deviceAddress, a.deviceAddress);
            }
        }



    }

where I get scan device results in a list. On clicking anyelement of that list I invoke AdapterView.OnItemClickListener(). Then how to send message to the device. How to read those log messages. How to know if the code has send message. How to receive message in other device.I have already done BLE scan, which working fine.But not added the code here.

the log cat report is as follows

FATAL EXCEPTION: main
                                                                                Process: com.example.root.securityalert, PID: 27945
                                                                                java.lang.NullPointerException: Attempt to read from field 'android.bluetooth.BluetoothGattCallback com.example.root.securityalert.BluetoothLeService.mGattCallback' on a null object reference
                                                                                    at com.example.root.securityalert.DeviceScanActivity$1.onItemClick(DeviceScanActivity.java:115)
                                                                                    at android.widget.AdapterView.performItemClick(AdapterView.java:310)
                                                                                    at android.widget.AbsListView.performItemClick(AbsListView.java:1156)
                                                                                    at android.widget.AbsListView$PerformClick.run(AbsListView.java:3147)
                                                                                    at android.widget.AbsListView$3.run(AbsListView.java:4062)
                                                                                    at android.os.Handler.handleCallback(Handler.java:751)
                                                                                    at android.os.Handler.dispatchMessage(Handler.java:95)
                                                                                    at android.os.Looper.loop(Looper.java:154)
                                                                                    at android.app.ActivityThread.main(ActivityThread.java:6165)
                                                                                    at java.lang.reflect.Method.invoke(Native Method)
                                                                                    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:888)
                                                                                    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:778)

I also tried with the code

    public class BluetoothSendRecv extends BluetoothGattCallback{

        public BluetoothManager mBluetoothManager;
        public BluetoothAdapter mBluetoothAdapter;
        public String mBluetoothDeviceAddress;
        public BluetoothGatt mBluetoothGatt;
        private int mConnectionState = STATE_DISCONNECTED;
        // private final IBinder mBinder = new BluetoothLeService.LocalBinder();
        public Context cntxt;
       .......
UUID.fromString(SampleGattAttributes.HEART_RATE_MEASUREMENT);

        // Various callback methods defined by the BLE API.
        public final BluetoothGattCallback mGattCallback =
                new BluetoothGattCallback() {
                    @Override
                    public void onConnectionStateChange(BluetoothGatt gatt, int status,
                                                        int newState) {
                        String intentAction;
                        if (newState == BluetoothProfile.STATE_CONNECTED) {
                            intentAction = ACTION_GATT_CONNECTED;
                            mConnectionState = STATE_CONNECTED;
                            broadcastUpdate(intentAction);
                            Log.i(TAG, "Connected to GATT server.");
                            Log.i(TAG, "Attempting to start service discovery:" +
                                    mBluetoothGatt.discoverServices());

                        } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                            intentAction = ACTION_GATT_DISCONNECTED;
                            mConnectionState = STATE_DISCONNECTED;
                            Log.i(TAG, "Disconnected from GATT server.");
                            broadcastUpdate(intentAction);
                        }
                    }

                    @Override
                    // New services discovered
                    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
                        if (status == BluetoothGatt.GATT_SUCCESS) {
                            //broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
                            for (BluetoothGattService gattService : gatt.getServices()) {
                                Log.i(TAG, "onServicesDiscovered: ---------------------");
                                Log.i(TAG, "onServicesDiscovered: service=" + gattService.getUuid());
                                for (BluetoothGattCharacteristic characteristic : gattService.getCharacteristics()) {
                                    Log.i(TAG, "onServicesDiscovered: characteristic=" + characteristic.getUuid());

                                    if (characteristic.getUuid().toString().equals("0000fee9-0000-1000-8000-00805f9b34fb"/*0000ffe9-0000-1000-8000-00805f9b34fb"*/)) {

                                        Log.w(TAG, "onServicesDiscovered: found LED");

                                        String originalString = "560D0F0600F0AA";

                                        byte[] b = hexStringToByteArray(originalString);

                                        characteristic.setValue(b); // call this BEFORE(!) you 'write' any stuff to the server
                                        mBluetoothGatt.writeCharacteristic(characteristic);
                                        //Toast.makeText(this., "R.string.error_bluetooth_not_supported", Toast.LENGTH_SHORT).show();
                                        //finish()
                                        System.out.println("Writing Mithun");
                                        Toast.makeText(cntxt, originalString, Toast.LENGTH_SHORT).show();
                                        Log.i(TAG, "onServicesDiscovered: , write bytes?! " + byteToHexStr(b));
                                    }
                                }
                            }

                            broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);

                        } else {
                            Log.w(TAG, "onServicesDiscovered received: " + status);
                        }
                    }

                    @Override
                    // Result of a characteristic read operation
                    public void onCharacteristicRead(BluetoothGatt gatt,
                                                     BluetoothGattCharacteristic characteristic,
                                                     int status) {
                        if (status == BluetoothGatt.GATT_SUCCESS) {
                            byte[] data = characteristic.getValue();
                            System.out.println("reading");
                            System.out.println(new String(data));
                            broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
                        }
                    }
                    //...
                };
        private void broadcastUpdate(final String action) {
            final Intent intent = new Intent(action);
            //sendBroadcast(intent);
        }

        .....
    }

Then logcat report  is as follows aaaa

D/BluetoothGatt: onClientConnectionState() - status=0 clientIf=6 device=CC:1E:39:39:62:1C
I/ContentValues: Connected to GATT server.
D/BluetoothGatt: discoverServices() - device: CC:1E:39:39:62:1C
I/ContentValues: Attempting to start service discovery:true
D/BluetoothGatt: onSearchComplete() = Device=CC:1E:39:39:62:1C Status=0
I/ContentValues: onServicesDiscovered: ---------------------
I/ContentValues: onServicesDiscovered: service=00001800-0000-1000-8000-00805f9b34fb
I/ContentValues: onServicesDiscovered: characteristic=00002a00-0000-1000-8000-00805f9b34fb


I think this condition is not fullfilled
if(characteristic.getUuid().toString().equals("0000fee9-0000-1000-8000-00805f9b34fb"/*0000ffe9-0000-1000-8000-00805f9b34fb"*/)) 
1

There are 1 answers

2
Mustafa Demir On

I think you must add "Scan" method before try connect.This is rule of ble architecture.Like this :

 private void startScanning(final boolean enable, BluetoothAdapter mBluetoothAdapter) {
    bluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();
    List<ScanFilter> scanFilters = new ArrayList<>();
    final ScanSettings settings = new ScanSettings.Builder().build();
    ScanFilter scanFilter = new ScanFilter.Builder().setServiceUuid(ParcelUuid.fromString(UUID_SERVICE)).build();
    scanFilters.add(scanFilter);
    bluetoothLeScanner.startScan(scanFilters, settings, scanCallback);

}

And if Ble Device is founded :

ScanCallback scanCallback = new ScanCallback() {
    @Override
    public void onScanResult(int callbackType, ScanResult result) {
        super.onScanResult(callbackType, result);
        bluetoothDevice = result.getDevice();
        Log.d(TAG, "onScanResult: bluetoothDevice.getAddress());
        mScanning = false;
        String adress = bluetoothDevice.getAddress();
        bluetoothLeScanner.stopScan(scanCallback);
        connect(bluetoothDevice);

    }