I have create BLE app which includes client and server app. The code is running successfully. Now i want to run server code as a service so that Bluetooth is open every time and it can be searched by client app whenever required. The code used for server is as below. Please guide me to use this code as a service.

public class ServerActivity extends AppCompatActivity {

private static final String TAG = "ServerActivity";

private ActivityServerBinding mBinding;

private Handler mHandler;
private Handler mLogHandler;
private List<BluetoothDevice> mDevices;
private Map<String, byte[]> mClientConfigurations;

private BluetoothGattServer mGattServer;
private BluetoothManager mBluetoothManager;
private BluetoothAdapter mBluetoothAdapter;
private BluetoothLeAdvertiser mBluetoothLeAdvertiser;

// Lifecycle

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    mHandler = new Handler();
    mLogHandler = new Handler(Looper.getMainLooper());
    mDevices = new ArrayList<>();
    mClientConfigurations = new HashMap<>();

    mBluetoothManager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
    mBluetoothAdapter = mBluetoothManager.getAdapter();

    mBinding = DataBindingUtil.setContentView(this, R.layout.activity_server);
    mBinding.restartServerButton.setOnClickListener(v -> restartServer());
}

@Override
protected void onResume() {
    super.onResume();

    // Check if bluetooth is enabled
    if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
        // Request user to enable it
        Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
        startActivity(enableBtIntent);
        finish();
        return;
    }

    // Check low energy support
    if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
        // Get a newer device
        log("No LE Support.");
        finish();
        return;
    }

    // Check advertising
    if (!mBluetoothAdapter.isMultipleAdvertisementSupported()) {
        // Unable to run the server on this device, get a better device
        log("No Advertising Support.");
        finish();
        return;
    }

    mBluetoothLeAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser();
    GattServerCallback gattServerCallback = new GattServerCallback();
    mGattServer = mBluetoothManager.openGattServer(this, gattServerCallback);

    Bundle bundle = getIntent().getExtras();
    String plate_name = bundle.getString("PLATE_NAME");

    mBluetoothAdapter.setName(plate_name);
    Toast.makeText(getApplicationContext(),"PLATE NAME "+plate_name,Toast.LENGTH_LONG).show();

    @SuppressLint("HardwareIds")
    String deviceInfo = "Device Info"
            + "\nName: " + mBluetoothAdapter.getName()
            + "\nAddress: " + mBluetoothAdapter.getAddress();
    mBinding.serverDeviceInfoTextView.setText(deviceInfo);

    setupServer();
    startAdvertising();
}

@Override
protected void onPause() {
    super.onPause();
    stopAdvertising();
    stopServer();
}

// GattServer
private void setupServer() {
    BluetoothGattService service = new BluetoothGattService(SERVICE_UUID, BluetoothGattService.SERVICE_TYPE_PRIMARY);

    // Write characteristic
    BluetoothGattCharacteristic writeCharacteristic = new BluetoothGattCharacteristic(CHARACTERISTIC_ECHO_UUID,
            BluetoothGattCharacteristic.PROPERTY_WRITE,
            // Somehow this is not necessary, the client can still enable notifications
        // | BluetoothGattCharacteristic.PROPERTY_NOTIFY,
            BluetoothGattCharacteristic.PERMISSION_WRITE);

    // Characteristic with Descriptor
    BluetoothGattCharacteristic notifyCharacteristic = new BluetoothGattCharacteristic(CHARACTERISTIC_TIME_UUID,
            // Somehow this is not necessary, the client can still enable notifications
        // BluetoothGattCharacteristic.PROPERTY_NOTIFY,
            0, 0);

    BluetoothGattDescriptor clientConfigurationDescriptor = new BluetoothGattDescriptor(CLIENT_CONFIGURATION_DESCRIPTOR_UUID,
            BluetoothGattDescriptor.PERMISSION_READ | BluetoothGattDescriptor.PERMISSION_WRITE);
    clientConfigurationDescriptor.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);

    notifyCharacteristic.addDescriptor(clientConfigurationDescriptor);

    service.addCharacteristic(writeCharacteristic);
    service.addCharacteristic(notifyCharacteristic);

    mGattServer.addService(service);
}

private void stopServer() {
    if (mGattServer != null) {
        mGattServer.close();
    }
}

private void restartServer() {
    stopAdvertising();
    stopServer();
    setupServer();
    startAdvertising();
}

// Advertising

private void startAdvertising() {
    if (mBluetoothLeAdvertiser == null) {
        return;
    }

    AdvertiseSettings settings = new AdvertiseSettings.Builder()
            .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED)
            .setConnectable(true)
            .setTimeout(0)
            .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_LOW)
            .build();

    ParcelUuid parcelUuid = new ParcelUuid(SERVICE_UUID);
    AdvertiseData data = new AdvertiseData.Builder()
            .setIncludeDeviceName(true)
            .addServiceUuid(parcelUuid)
            .build();

    mBluetoothLeAdvertiser.startAdvertising(settings, data, mAdvertiseCallback);
}

private void stopAdvertising() {
    if (mBluetoothLeAdvertiser != null) {
        mBluetoothLeAdvertiser.stopAdvertising(mAdvertiseCallback);
    }
}

private AdvertiseCallback mAdvertiseCallback = new AdvertiseCallback() {
    @Override
    public void onStartSuccess(AdvertiseSettings settingsInEffect) {
        log("Peripheral advertising started.");
    }

    @Override
    public void onStartFailure(int errorCode) {
        log("Peripheral advertising failed: " + errorCode);
    }
};

// Notifications

private void notifyCharacteristicTime(byte[] value) {
    notifyCharacteristic(value, CHARACTERISTIC_TIME_UUID);
}

private void notifyCharacteristic(byte[] value, UUID uuid) {
    mHandler.post(() -> {
        BluetoothGattService service = mGattServer.getService(SERVICE_UUID);
        BluetoothGattCharacteristic characteristic = service.getCharacteristic(uuid);
        log("Notifying characteristic " + characteristic.getUuid().toString()
                + ", new value: " + StringUtils.byteArrayInHexFormat(value));

        characteristic.setValue(value);
        // Indications require confirmation, notifications do not
        boolean confirm = BluetoothUtils.requiresConfirmation(characteristic);
        for (BluetoothDevice device : mDevices) {
            if (clientEnabledNotifications(device, characteristic)) {
                mGattServer.notifyCharacteristicChanged(device, characteristic, confirm);
            }
        }
    });
}

private boolean clientEnabledNotifications(BluetoothDevice device, BluetoothGattCharacteristic characteristic) {
    List<BluetoothGattDescriptor> descriptorList = characteristic.getDescriptors();
    BluetoothGattDescriptor descriptor = BluetoothUtils.findClientConfigurationDescriptor(descriptorList);
    if (descriptor == null) {
        // There is no client configuration descriptor, treat as true
        return true;
    }
    String deviceAddress = device.getAddress();
    byte[] clientConfiguration = mClientConfigurations.get(deviceAddress);
    if (clientConfiguration == null) {
        // Descriptor has not been set
        return false;
    }

    byte[] notificationEnabled = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE;
    return clientConfiguration.length == notificationEnabled.length
            && (clientConfiguration[0] & notificationEnabled[0]) == notificationEnabled[0]
            && (clientConfiguration[1] & notificationEnabled[1]) == notificationEnabled[1];
}

// Gatt Server Actions

public void log(String msg) {
    Log.d(TAG, msg);
    mLogHandler.post(() -> {
        mBinding.viewServerLog.logTextView.append(msg + "\n");
        mBinding.viewServerLog.logScrollView.post(() -> mBinding.viewServerLog.logScrollView.fullScroll(View.FOCUS_DOWN));
        if (msg.contains("Open")) {
            ToneGenerator toneGen1 = new ToneGenerator(AudioManager.STREAM_MUSIC, 100);
            toneGen1.startTone(ToneGenerator.TONE_CDMA_PIP, 150);
        } else if (msg.contains("Close")) {
            ToneGenerator toneGen1 = new ToneGenerator(AudioManager.STREAM_MUSIC, 100);
            toneGen1.startTone(ToneGenerator.TONE_CDMA_PIP, 150);
            toneGen1.startTone(ToneGenerator.TONE_CDMA_PIP, 300);
        }
    });
}

public void addDevice(BluetoothDevice device) {
    log("Device added: " + device.getAddress());
    mHandler.post(() -> mDevices.add(device));
}

public void removeDevice(BluetoothDevice device) {
    log("Device removed: " + device.getAddress());
    mHandler.post(() -> {
        mDevices.remove(device);
        String deviceAddress = device.getAddress();
        mClientConfigurations.remove(deviceAddress);
    });
}

public void addClientConfiguration(BluetoothDevice device, byte[] value) {
    String deviceAddress = device.getAddress();
    mClientConfigurations.put(deviceAddress, value);

}

public void sendResponse(BluetoothDevice device, int requestId, int status, int offset, byte[] value) {
    mGattServer.sendResponse(device, requestId, status, 0, null);
}

public void notifyCharacteristicEcho(byte[] value) {
    notifyCharacteristic(value, CHARACTERISTIC_ECHO_UUID);
}

// Gatt Callback

private class GattServerCallback extends BluetoothGattServerCallback {

    @Override
    public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
        super.onConnectionStateChange(device, status, newState);
        log("onConnectionStateChange " + device.getAddress()
                + "\nstatus " + status
                + "\nnewState " + newState);

        if (newState == BluetoothProfile.STATE_CONNECTED) {
            addDevice(device);
        } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
            removeDevice(device);
        }
    }

    // The Gatt will reject Characteristic Read requests that do not have the permission set,
    // so there is no need to check inside the callback
    @Override
    public void onCharacteristicReadRequest(BluetoothDevice device,
                                            int requestId,
                                            int offset,
                                            BluetoothGattCharacteristic characteristic) {
        super.onCharacteristicReadRequest(device, requestId, offset, characteristic);

        log("onCharacteristicReadRequest " + characteristic.getUuid().toString());

        if (BluetoothUtils.requiresResponse(characteristic)) {
            // Unknown read characteristic requiring response, send failure
            sendResponse(device, requestId, BluetoothGatt.GATT_FAILURE, 0, null);
        }
        // Not one of our characteristics or has NO_RESPONSE property set
    }

    // The Gatt will reject Characteristic Write requests that do not have the permission set,
    // so there is no need to check inside the callback
    @Override
    public void onCharacteristicWriteRequest(BluetoothDevice device,
                                             int requestId,
                                             BluetoothGattCharacteristic characteristic,
                                             boolean preparedWrite,
                                             boolean responseNeeded,
                                             int offset,
                                             byte[] value) {
        super.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value);
        log("onCharacteristicWriteRequest" + characteristic.getUuid().toString()
                + "\nReceived: " + StringUtils.stringFromBytes(value));

        if (CHARACTERISTIC_ECHO_UUID.equals(characteristic.getUuid())) {
            sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, null);

            // Reverse message to differentiate original message & response
            byte[] response = ByteUtils.reverse(value);
            characteristic.setValue(response);
            log("Sending: " + StringUtils.byteArrayInHexFormat(response));
            notifyCharacteristicEcho(response);
        }
    }

    // The Gatt will reject Descriptor Read requests that do not have the permission set,
    // so there is no need to check inside the callback
    @Override
    public void onDescriptorReadRequest(BluetoothDevice device,
                                        int requestId,
                                        int offset,
                                        BluetoothGattDescriptor descriptor) {
        super.onDescriptorReadRequest(device, requestId, offset, descriptor);
        log("onDescriptorReadRequest" + descriptor.getUuid().toString());
    }

    // The Gatt will reject Descriptor Write requests that do not have the permission set,
    // so there is no need to check inside the callback
    @Override
    public void onDescriptorWriteRequest(BluetoothDevice device,
                                         int requestId,
                                         BluetoothGattDescriptor descriptor,
                                         boolean preparedWrite,
                                         boolean responseNeeded,
                                         int offset,
                                         byte[] value) {
        super.onDescriptorWriteRequest(device, requestId, descriptor, preparedWrite, responseNeeded, offset, value);
        log("onDescriptorWriteRequest: " + descriptor.getUuid().toString()
                + "\nvalue: " + StringUtils.stringFromBytes(value));

        if (CLIENT_CONFIGURATION_DESCRIPTOR_UUID.equals(descriptor.getUuid())) {
            addClientConfiguration(device, value);
            sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, null);
        }
    }

    @Override
    public void onNotificationSent(BluetoothDevice device, int status) {
        super.onNotificationSent(device, status);
        log("onNotificationSent");
    }
}
}

1 Answers

0
Rahul Rastogi On Best Solutions

Use Foreground service with persistent notification. Your service will be keep on running.