I’m working on an Android app that pairs with a Raspberry Pi-based Bluetooth device using the Bluetooth serial connection. I’m attempting to send notifications to the device once it’s paired, but I am running into issues where the connection isn’t established properly, and the Bluetooth device doesn’t respond as expected.
It shows up on my Android Bluetooth settings, and when I click, it pairs without the need of a passkey, but then on the Pi, it shows no longer connected, but still paired on the phone, not connected.
Raspberry Pi Setup: I have configured the Raspberry Pi to be discoverable and pairable using the bluetoothctl commands, without a passkey, and it runs auto on boot: It is also set as a “Device” not a Host.
bash
Copy code
sudo bluetoothctl
power on
agent NoInputNoOutput
default-agent
discoverable on
pairable on
This is my FirstFragment Android Studio Java:
package com.aaadev.myapplication;
import android.Manifest;
import android.annotation.SuppressLint;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.ActivityCompat;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
import androidx.fragment.app.Fragment;
import com.aaadev.myapplication.databinding.FragmentFirstBinding;
import java.util.ArrayList;
import java.util.Set;
public class FirstFragment extends Fragment {
private Button buttonSendNotification;
private static final int REQUEST_BLUETOOTH_PERMISSIONS = 1;
private FragmentFirstBinding binding;
private BluetoothAdapter bluetoothAdapter;
private ArrayList<String> deviceList;
private ArrayList<BluetoothDevice> pairedDevicesList;
private ListView listViewDevices;
private static final String CHANNEL_ID = "app_push_notification_channel";
@Override
public View onCreateView(@NonNull android.view.LayoutInflater inflater, @Nullable android.view.ViewGroup container, @Nullable Bundle savedInstanceState) {
binding = FragmentFirstBinding.inflate(inflater, container, false);
return binding.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
buttonSendNotification = binding.buttonSendNotification;
listViewDevices = binding.listViewDevices;
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
deviceList = new ArrayList<>();
pairedDevicesList = new ArrayList<>();
// Check Bluetooth permissions and list paired devices
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
if (ActivityCompat.checkSelfPermission(getContext(), Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[]{Manifest.permission.BLUETOOTH_CONNECT}, REQUEST_BLUETOOTH_PERMISSIONS);
} else {
listPairedDevices();
}
} else {
listPairedDevices();
}
// Button click listener to send a push notification
buttonSendNotification.setOnClickListener(v -> sendPushNotification());
// ListView item click listener to select a device
listViewDevices.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
BluetoothDevice selectedDevice = pairedDevicesList.get(position);
@SuppressLint("MissingPermission") String deviceName = selectedDevice.getName();
if (deviceName != null && deviceName.contains("Pi")) {
// Start NotificationService and pass the device's address
Intent intent = new Intent(getActivity(), NotificationService.class);
intent.putExtra("device_address", selectedDevice.getAddress());
getActivity().startService(intent);
Toast.makeText(getContext(), "Connecting to " + deviceName, Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(getContext(), "Selected device is not a Pi device", Toast.LENGTH_SHORT).show();
}
}
});
}
@SuppressLint("MissingPermission")
private void listPairedDevices() {
if (bluetoothAdapter != null) {
@SuppressLint("MissingPermission") Set<BluetoothDevice> pairedDevices = bluetoothAdapter.getBondedDevices();
if (pairedDevices.size() > 0) {
for (BluetoothDevice device : pairedDevices) {
pairedDevicesList.add(device);
deviceList.add(device.getName() + "n" + device.getAddress());
}
ArrayAdapter<String> adapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_list_item_1, deviceList);
listViewDevices.setAdapter(adapter);
} else {
Toast.makeText(getContext(), "No paired devices found", Toast.LENGTH_SHORT).show();
}
} else {
Toast.makeText(getContext(), "Bluetooth not supported on this device", Toast.LENGTH_SHORT).show();
}
}
@SuppressLint("MissingPermission")
private void sendPushNotification() {
createNotificationChannel();
NotificationCompat.Builder builder = new NotificationCompat.Builder(getContext(), CHANNEL_ID)
.setSmallIcon(android.R.drawable.stat_notify_chat)
.setContentTitle("New Push Notification")
.setContentText("This is a push notification from YapYap!")
.setPriority(NotificationCompat.PRIORITY_HIGH);
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(getContext());
notificationManager.notify(1, builder.build());
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_BLUETOOTH_PERMISSIONS) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
listPairedDevices();
} else {
Toast.makeText(getContext(), "Bluetooth permission denied", Toast.LENGTH_SHORT).show();
}
}
}
private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
CharSequence name = "App Push Notifications";
String description = "Channel for app push notifications";
int importance = NotificationManager.IMPORTANCE_HIGH;
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, name, importance);
channel.setDescription(description);
NotificationManager notificationManager = getContext().getSystemService(NotificationManager.class);
if (notificationManager != null) {
notificationManager.createNotificationChannel(channel);
}
}
}
@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
}
Here is the Notification Sevrer.java:
package com.aaadev.myapplication;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Service;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.IBinder;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.core.app.ActivityCompat;
import androidx.core.app.NotificationCompat;
import java.io.IOException;
import java.io.OutputStream;
import java.util.UUID;
public class NotificationService extends Service {
private static final String CHANNEL_ID = "notification_service_channel";
public static final String ACTION_CONNECTION_STATUS = "com.aaadev.myapplication.CONNECTION_STATUS";
public static final String EXTRA_CONNECTION_SUCCESS = "connection_success";
private BluetoothSocket bluetoothSocket;
private OutputStream outputStream;
@Override
public void onCreate() {
super.onCreate();
createNotificationChannel();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
String deviceAddress = intent.getStringExtra("device_address");
if (deviceAddress != null) {
new Thread(() -> connectToDevice(deviceAddress)).start(); // Run on background thread
}
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Pi Notification Service")
.setContentText("Attempting to connect to Pi device...")
.setSmallIcon(android.R.drawable.stat_notify_chat)
.build();
startForeground(1, notification);
return START_NOT_STICKY;
}
private void connectToDevice(String address) {
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (!checkBluetoothPermission()) {
Log.e("NotificationService", "Missing BLUETOOTH_CONNECT permission.");
broadcastConnectionStatus(false);
stopSelf();
return;
}
BluetoothDevice device = bluetoothAdapter.getRemoteDevice(address);
Log.d("NotificationService", "Attempting to connect to device: " + device.getName() + " (" + address + ")");
try {
Log.d("NotificationService", "Creating BluetoothSocket...");
bluetoothSocket = device.createRfcommSocketToServiceRecord(UUID.fromString("00001800-0000-1000-8000-00805f9b34fb"));
Log.d("NotificationService", "Connecting to BluetoothSocket...");
bluetoothSocket.connect(); // Attempt to connect
Log.d("NotificationService", "BluetoothSocket connected successfully!");
outputStream = bluetoothSocket.getOutputStream();
// Broadcast success status
broadcastConnectionStatus(true);
} catch (IOException e) {
Log.e("NotificationService", "Connection failed: " + e.getMessage(), e);
handleConnectionFailure();
}
}
private boolean checkBluetoothPermission() {
return Build.VERSION.SDK_INT < Build.VERSION_CODES.S ||
ActivityCompat.checkSelfPermission(this, android.Manifest.permission.BLUETOOTH_CONNECT) == PackageManager.PERMISSION_GRANTED;
}
private void handleConnectionFailure() {
if (bluetoothSocket != null) {
try {
Log.d("NotificationService", "Closing BluetoothSocket due to failure...");
bluetoothSocket.close();
} catch (IOException closeException) {
Log.e("NotificationService", "Failed to close BluetoothSocket: " + closeException.getMessage(), closeException);
}
}
broadcastConnectionStatus(false);
stopSelf();
}
private void broadcastConnectionStatus(boolean success) {
Intent broadcastIntent = new Intent(ACTION_CONNECTION_STATUS);
broadcastIntent.putExtra(EXTRA_CONNECTION_SUCCESS, success);
sendBroadcast(broadcastIntent);
}
private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel serviceChannel = new NotificationChannel(
CHANNEL_ID,
"Notification Service Channel",
NotificationManager.IMPORTANCE_DEFAULT
);
NotificationManager manager = getSystemService(NotificationManager.class);
if (manager != null) {
manager.createNotificationChannel(serviceChannel);
}
}
}
@Override
public void onDestroy() {
super.onDestroy();
try {
if (outputStream != null) outputStream.close();
if (bluetoothSocket != null) bluetoothSocket.close();
} catch (Exception e) {
Log.e("NotificationService", "Error closing resources: " + e.getMessage(), e);
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
The “Pi” shows up on my listview and when i click it, it says Connecting on a toast, but this error throws:
Attempting to connect to device: Pi (XX:XX:XX:D2:XX:XX)
2024-12-15 13:53:42.781 27103-27156 NotificationService com.aaadev.myapplication D Creating BluetoothSocket...
2024-12-15 13:53:42.782 27103-27156 NotificationService com.aaadev.myapplication D Connecting to BluetoothSocket...
2024-12-15 13:53:42.782 27103-27156 BluetoothSocket com.aaadev.myapplication I connect() for device XX:XX:XX:XX:5D:XX called by pid: 27103
2024-12-15 13:53:47.172 27103-27196 ProfileInstaller com.aaadev.myapplication D Installing profile for com.aaadev.myapplication
2024-12-15 13:53:47.868 27103-27156 NotificationService com.aaadev.myapplication E Connection failed: read failed, socket might closed or timeout, read ret: -1
java.io.IOException: read failed, socket might closed or timeout, read ret: -1
at android.bluetooth.BluetoothSocket.readAll(BluetoothSocket.java:1054)
at android.bluetooth.BluetoothSocket.readInt(BluetoothSocket.java:1068)
at android.bluetooth.BluetoothSocket.connect(BluetoothSocket.java:583)
at com.aaadev.myapplication.NotificationService.connectToDevice(NotificationService.java:74)
at com.aaadev.myapplication.NotificationService.lambda$onStartCommand$0$com-aaadev-myapplication-NotificationService(NotificationService.java:43)
at com.aaadev.myapplication.NotificationService$$ExternalSyntheticLambda0.run(Unknown Source:4)
at java.lang.Thread.run(Thread.java:1012)
2024-12-15 13:53:47.868 27103-27156 NotificationService com.aaadev.myapplication D Closing BluetoothSocket due to failure...
I don’t know why the Pi wont connect… I need to have it receive Push Notifications to the Pi from the phone from a foreground service / background service.
Let me know why this error is showing up.
Thanks in advance.
2