skip to Main Content

I am working on Nordic NRF52840 periodic advertising and scan the device with Android app Android 10. I ask a question yesterday then I rewrite my code using the tutorial on the internet, basically my app is very easy, press the button then the app will scan and print the ble device on log, but I still can’t find my device I don’t know what’s wrong with my code I add a ton of permission in Manifest.xml but nothing changed I still can’t find the device I want. Update: I can find some device,but not my device, I still can’t find my device there are a few question I want to ask

  1. I can’t find my device, but can find other device.
  2. how to stop scanning, I add a button to stop scanning, but it won’t stop.
  3. how to get device name, when I call getName() error pop up and the app closed
    getName error

AndroidManifest

<?xml version="1.0" encoding="utf-8"?>
<manifest 
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.tryble_scanner">

<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission 
android:name="android.permission.BLUETOOTH_ADMIN"/>

<uses-permission 
android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" 
 />
<uses-permission 
android:name="android.permission.BLUETOOTH_ADVERTISE" />

<uses-permission 
android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission 
android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission 
android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
<uses-feature android:name="android.hardware.bluetooth" 
android:required="true"/>
<uses-feature android:name="android.hardware.bluetooth_le" 
android:required="true"/>

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/Theme.TryBle_scanner">
    <activity android:name=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category 
android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
</application>

</manifest>

MainActivity.java

package com.example.tryble_scanner;

import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.AppCompatActivity;

import android.Manifest;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;


import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

public class MainActivity extends AppCompatActivity {

private BluetoothAdapter mBluetoothAdapter  = null;
private BluetoothLeScanner mBluetoothLeScanner = null;
public static final int REQUEST_BT_PERMISSIONS = 0;
public static final int REQUEST_BT_ENABLE = 1;
private boolean mScanning = false;
private Handler mHandler = null;

private ScanCallback mLeScanCallback = new ScanCallback() {
            @Override
            public void onScanResult(int callbackType, final ScanResult result) {
                //super.onScanResult(callbackType, result);
                BluetoothDevice btdevice = result.getDevice();
                Log.d("BLE", btdevice.getAddress());
            }
            @Override
            public void onScanFailed(int errorCode) {
                super.onScanFailed(errorCode);
                Log.d("BLE", "error");
            }
};
private  ScanCallback mLeScanCallback2=new ScanCallback() {
    @Override
    public void onScanResult(int callbackType, ScanResult result) {
        Log.d("BLE","scan stop");
    }
    @Override
    public void onScanFailed(int errorCode) {
        Log.d("BLE","stop scan failed");
    }
};
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Button btnScan = (Button) findViewById(R.id.btnScan);
    BluetoothManager bluetoothManager = getSystemService(BluetoothManager.class);
    mBluetoothAdapter = bluetoothManager.getAdapter();
    mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();
    this.mHandler = new Handler();
}

public void stop_scan(View view) {
    Log.d("Ble","scan stop pressed");
//        mBluetoothLeScanner.stopScan(mLeScanCallback2);
mBluetoothAdapter.getBluetoothLeScanner().stopScan(mLeScanCallback2);
}

public void onBtnScan(View view) {
    Log.i("Btn","get click");
    checkBTPermission();
    String[] names=new String[]{"Auden test"};
    List<ScanFilter> filters=null;
    if(names != null){
        filters=new ArrayList<>();
        for(String name:names){
            ScanFilter filter=new ScanFilter.Builder().setDeviceName(name).build();
            filters.add(filter);
        }
    }
    if(mBluetoothLeScanner==null){
        Log.i("BLE","could not get scanner");
    }else{
        mBluetoothLeScanner.startScan(mLeScanCallback);  //filters,scanSettings,mLeScanCallback
    }
}

private void checkBTPermission(){
    if(Build.VERSION.SDK_INT>Build.VERSION_CODES.LOLLIPOP){
        int pc=this.checkSelfPermission("Manifest.permission.ACCESS_FINE_LOCATION");
        pc+=this.checkSelfPermission("Manifest.permission.ACCESS_COARSE_LOCATION");
        if(pc!=0){
            this.requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION,Manifest.permission.ACCESS_COARSE_LOCATION},1001);
        }else {
            Log.d("BLE","checkBT permission");
        }
    }
}
}

3

Answers


  1. You do indeed declare the permissions in your manifest file, but keep in mind that some of them are so called runtime permissions. You need to ask the user of your app for the permission at runtime. The Request app permissions site of the Android Developer Guide gives you more information on that topic.

    You basically need a permissions callback to handle the users response:

    // Register the permissions callback, which handles the user's response to the
    // system permissions dialog. Save the return value, an instance of
    // ActivityResultLauncher, as an instance variable.
    private ActivityResultLauncher<String> requestPermissionLauncher =
        registerForActivityResult(new RequestPermission(), isGranted -> {
            if (isGranted) {
                // Permission is granted. Continue the action or workflow in your
                // app.
            } else {
                // Explain to the user that the feature is unavailable because the
                // features requires a permission that the user has denied. At the
                // same time, respect the user's decision. Don't link to system
                // settings in an effort to convince the user to change their
                // decision.
            }
        });
    

    You also need to actually ask the user for the permission:

    if (ContextCompat.checkSelfPermission(
            CONTEXT, Manifest.permission.REQUESTED_PERMISSION) ==
            PackageManager.PERMISSION_GRANTED) {
        // You can use the API that requires the permission.
        performAction(...);
    } else if (shouldShowRequestPermissionRationale(...)) {
        // In an educational UI, explain to the user why your app requires this
        // permission for a specific feature to behave as expected. In this UI,
        // include a "cancel" or "no thanks" button that allows the user to
        // continue using your app without granting the permission.
        showInContextUI(...);
    } else {
        // You can directly ask for the permission.
        // The registered ActivityResultCallback gets the result of this request.
        requestPermissionLauncher.launch(
                Manifest.permission.REQUESTED_PERMISSION);
    }
    

    The one permission that’s most likely missing is the Location permission. This part of the Developer Guide lists the required permissions for BLE on Android < 11.

    Please also use a generic BLE scanner such as nRF Connect to verify that your device actually advertises itself.

    Login or Signup to reply.
  2. If you’re done with permissions and the rest, probably you are instantiating the BluetoothAdapter in an incorrect way. Try instantiating it like the following example in your main activity’s onCreate method:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btnScan = (Button) findViewById(R.id.btnScan);
        BluetoothManager bluetoothManager = getSystemService(BluetoothManager.class);
        mBluetoothAdapter = bluetoothManager.getAdapter();
        mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();
        mHandler = new Handler();
    
    }
    

    Update for test

    Well I had to make a new basic project to test it. I’ll share te manifest and activity code, also the output. I’ve tested it on my Android 11 installed mobile. Note that I’ve removed all scan filters.

    manifest

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        package="com.example.karalama">
    
    
        <uses-permission android:name="android.permission.BLUETOOTH"/>
        <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
    
        <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
        <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
        <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
    
        <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
        <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
        <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
        <uses-feature android:name="android.hardware.bluetooth" android:required="true"/>
        <uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
        <application
            android:allowBackup="true"
            android:dataExtractionRules="@xml/data_extraction_rules"
            android:fullBackupContent="@xml/backup_rules"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:roundIcon="@mipmap/ic_launcher_round"
            android:supportsRtl="true"
            android:theme="@style/Theme.Karalama"
            tools:targetApi="31">
            <activity
                android:name=".MainActivity"
                android:exported="true"
                android:label="@string/app_name"
                android:theme="@style/Theme.Karalama.NoActionBar">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
        </application>
    
    </manifest>
    

    MainActivity

    package com.example.karalama;
    
    import android.Manifest;
    import android.bluetooth.BluetoothAdapter;
    import android.bluetooth.BluetoothDevice;
    import android.bluetooth.BluetoothManager;
    import android.bluetooth.le.BluetoothLeScanner;
    import android.bluetooth.le.ScanCallback;
    import android.bluetooth.le.ScanFilter;
    import android.bluetooth.le.ScanResult;
    import android.os.Build;
    import android.os.Bundle;
    
    import com.google.android.material.snackbar.Snackbar;
    
    import androidx.appcompat.app.AppCompatActivity;
    
    import android.util.Log;
    import android.view.View;
    
    import androidx.navigation.NavController;
    import androidx.navigation.Navigation;
    import androidx.navigation.ui.AppBarConfiguration;
    import androidx.navigation.ui.NavigationUI;
    
    import com.example.karalama.databinding.ActivityMainBinding;
    
    import android.view.Menu;
    import android.view.MenuItem;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class MainActivity extends AppCompatActivity {
    
        private AppBarConfiguration appBarConfiguration;
    
        private BluetoothAdapter mBluetoothAdapter  = null;
        private BluetoothLeScanner mBluetoothLeScanner = null;
        private boolean mScanning = false;
    
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            com.example.karalama.databinding.ActivityMainBinding binding = ActivityMainBinding.inflate(getLayoutInflater());
            setContentView(binding.getRoot());
    
            setSupportActionBar(binding.toolbar);
    
            NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
            appBarConfiguration = new AppBarConfiguration.Builder(navController.getGraph()).build();
            NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
    
            BluetoothManager bluetoothManager = getSystemService(BluetoothManager.class);
            mBluetoothAdapter = bluetoothManager.getAdapter();
            Log.d("BLE", "onCreate: Bluetoothadapter null"+(mBluetoothAdapter == null));
            mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();
    
            binding.fab.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    checkBTPermission();
                    String[] names=new String[]{"Auden test"};
                    List<ScanFilter> filters=null;
                    if(names != null){
                        filters=new ArrayList<>();
                        for(String name:names){
                            ScanFilter filter=new ScanFilter.Builder().setDeviceName(name).build();
                            filters.add(filter);
                        }
                    }
                    if(mBluetoothLeScanner==null){
                        Log.i("BLE","could not get scanner");
                    }else{
                        mBluetoothLeScanner.startScan(mLeScanCallback);
                    }
                }
            });
        }
    
        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
            // Inflate the menu; this adds items to the action bar if it is present.
            getMenuInflater().inflate(R.menu.menu_main, menu);
            return true;
        }
    
        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
            // Handle action bar item clicks here. The action bar will
            // automatically handle clicks on the Home/Up button, so long
            // as you specify a parent activity in AndroidManifest.xml.
            int id = item.getItemId();
    
            //noinspection SimplifiableIfStatement
            if (id == R.id.action_settings) {
                return true;
            }
    
            return super.onOptionsItemSelected(item);
        }
    
        @Override
        public boolean onSupportNavigateUp() {
            NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
            return NavigationUI.navigateUp(navController, appBarConfiguration)
                    || super.onSupportNavigateUp();
        }
    
        private final ScanCallback mLeScanCallback = new ScanCallback() {
    
            @Override
            public void onScanResult(int callbackType, final ScanResult result) {
                //super.onScanResult(callbackType, result);
                BluetoothDevice btdevice = result.getDevice();
                Log.d("BLE", "Found -> "+btdevice.getAddress());
            }
    
            @Override
            public void onScanFailed(int errorCode) {
                super.onScanFailed(errorCode);
                Log.d("BLE", "error");
            }
        };
    
        private void checkBTPermission(){
            if(Build.VERSION.SDK_INT>Build.VERSION_CODES.LOLLIPOP){
                int pc=this.checkSelfPermission("Manifest.permission.ACCESS_FINE_LOCATION");
                pc+=this.checkSelfPermission("Manifest.permission.ACCESS_COARSE_LOCATION");
                if(pc!=0){
                    this.requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION,Manifest.permission.ACCESS_COARSE_LOCATION},1001);
                }else {
                    Log.d("BLE","checkBT permission");
                }
            }
        }
    }
    

    The logcat output

    I/RenderThread: type=1400 audit(0.0:1130983): avc: denied { ioctl } for uid=10254 path="/dev/kgsl-3d0" dev="tmpfs" ino=15422 ioctlcmd=945 scontext=u:r:untrusted_app:s0:c254,c256,c512,c768 tcontext=u:object_r:device:s0 tclass=chr_file permissive=1 app=com.example.karalama
    I/RenderThread: type=1400 audit(0.0:1130984): avc: denied { read write } for uid=10254 path="/dev/kgsl-3d0" dev="tmpfs" ino=15422 scontext=u:r:untrusted_app:s0:c254,c256,c512,c768 tcontext=u:object_r:device:s0 tclass=chr_file permissive=1 app=com.example.karalama
    D/BluetoothAdapter: isLeEnabled(): ON
    D/BluetoothLeScanner: onScannerRegistered() - status=0 scannerId=6 mScannerId=0
    D/BLE: Found -> C8:FD:19:7F:5A:7D
    D/BLE: Found -> 5F:EE:47:E9:F3:A3
    D/BLE: Found -> D4:36:39:B5:D3:5D
    D/BLE: Found -> D4:36:39:8B:C1:98
    D/BLE: Found -> 40:94:41:26:D2:D6
    D/BLE: Found -> D4:36:39:8A:A9:AB
    D/BLE: Found -> D4:36:39:8B:C1:98
    D/BLE: Found -> 5F:EE:47:E9:F3:A3
    D/BLE: Found -> 4B:A2:87:44:4C:79
    D/BLE: Found -> D4:36:39:8A:A9:AB
    D/BLE: Found -> D4:36:39:8B:C1:98
    D/BLE: Found -> D4:36:39:B5:D3:5D
    D/BLE: Found -> 40:94:41:26:D2:D6
    D/BLE: Found -> 4B:A2:87:44:4C:79
    D/BLE: Found -> C8:FD:19:7F:5A:7D
    D/BLE: Found -> D4:36:39:B5:D3:5D
    D/BLE: Found -> D4:36:39:8B:C1:98
    D/BLE: Found -> 7A:0F:6B:DD:EC:FB
    
    

    Update 2 – Get rid of the "getName" error

    @Override
    public void onScanResult(int callbackType, final ScanResult result) {
        //super.onScanResult(callbackType, result);
        BluetoothDevice btdevice = result.getDevice();
    
        // The device may not have a name and getName may return null. Fix it in the following way
        String name = btdevice.getName() == null ? "No name" : btdevice.getName();
        Log.d("BLE", name+" - "+btdevice.getAddress());
    }
    
    Login or Signup to reply.
  3. you can take help from this link
    https://punchthrough.com/android-ble-guide/
    if this still not works for use than u can use scan library
    https://github.com/NordicSemiconductor/Android-Scanner-Compat-Library

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search