фоновое сканирование BLE, иногда bluetoothAdapter.isEnabled() имеет значение false. Почему это происходит?

фоновое сканирование BLE, иногда bluetoothAdapter.isEnabled() имеет значение false. Почему это происходит?

Сначала я попробовал фоновое сканирование BLE. (Это периодически.)

Приложение использует много фона, но мой клиент хочет именно этого.

Моя реализация была выполнена следующими способами:

  1. Я пробуждаю приложение каждый час с помощью диспетчера будильника.

  2. Если получен обратный вызов от широковещательного приемника приложения, запланируйте следующий сигнал тревоги, и WorkManager выполнит его с ускоренной опцией.

  3. Запустите сканирование BLE 40 секунд -> отправьте данные устройства BLE на сервер.

Манифест.xml

          <uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
    <uses-feature android:name="android.hardware.location.gps" android:required="true" />

    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

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


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

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

    <uses-permission android:name="android.permission.BLUETOOTH_SCAN"
        android:usesPermissionFlags="neverForLocation"
        tools:targetApi="s" />


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


    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />


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

scanCallbackImpl.java

      public class ScanCallbackImpl extends ScanCallback {
    private final PublishSubject<ScanResult> onScanResult$;
    private final Context context;
    private static final String SCAN_TEST = "SCAN_TEST";

    public ScanCallbackImpl(Context context) {
        super();
        this.context = context;
        onScanResult$ = PublishSubject.create();
    }


    @Override
    public void onScanResult(int callbackType, ScanResult result) {
        super.onScanResult(callbackType, result);
        Log.d(SCAN_TEST, "onScanResult");
        onScanResult$.onNext(result);
    }

    @Override
    public void onBatchScanResults(List<ScanResult> results) {
        super.onBatchScanResults(results);
        Log.d(SCAN_TEST, "onBatchScanResult");
    }


    @Override
    public void onScanFailed(int errorCode) {
        super.onScanFailed(errorCode);
        Log.d(SCAN_TEST, "onScanFailed");
        switch (errorCode) {
            case ScanCallback.SCAN_FAILED_ALREADY_STARTED:
                onScanResult$.onError(new AlreadyStartedException(context));
                break;
            case ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED:
                onScanResult$.onError(new ApplicationRegistrationFailedException(context));
                break;
            case ScanCallback.SCAN_FAILED_INTERNAL_ERROR:
                onScanResult$.onError(new InternalErrorException(context));
                break;
            case ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED:
                onScanResult$.onError(new FeatureUnsupportedException(context));
                break;
        }
    }

    public PublishSubject<ScanResult> getOnScanResult$() {
        return onScanResult$;
    }

}

ScanService.java

      private void scanClose() {
        if(onScanResult != null && !onScanResult.isDisposed()){
            onScanResult.dispose();
        }

        if(startScan != null && !startScan.isDisposed()){
            startScan.dispose();
        }
    }

public Observable<ScanDeviceVo> startScan(String deviceName, String macAddress, long timeoutSec, ScanMode scanMode) {
        scanClose();
        Observable<ScanResult> observable = Observable.create(emitter -> {
            if(!checkBleScanSupport()){
                emitter.onError(new BleScanUnsupportedException(context));
                return;
            }

            if(!checkEnable(emitter)){
                return;
            }

            if(!isAlwaysLocation()){
                emitter.onError(new LocationPermissionException(context));
                return;
            }

            BluetoothLeScanner bleScanner = bluetoothAdapter.getBluetoothLeScanner();

            scanCallback = new ScanCallbackImpl(context);
            scanCallback.getOnScanResult$()
                    .subscribeOn(Schedulers.io())
                    .observeOn(Schedulers.io())
                    .doOnSubscribe(disposable -> onScanResult = disposable)
                    .subscribe(emitter::onNext, emitter::onError);

            isScanning = true;

    
            bleScanner.startScan(setScanFilterList(deviceName, macAddress), setScanSetting(scanMode), scanCallback);
        });

        return observable
                .observeOn(Schedulers.io())
                .take(timeoutSec, TimeUnit.SECONDS)
                .timeout(timeoutSec, TimeUnit.SECONDS)
                .onErrorResumeNext(throwable -> throwable instanceof TimeoutException ? Observable.error(new NotFoundException(context)) : Observable.error(throwable))
                .distinct(scanResult -> scanResult.getScanRecord().getBytes())
                .map(scanResult -> ScanDeviceVo.builder()
                        .name(scanResult.getDevice().getName())
                        .macAddr(scanResult.getDevice().getAddress())
                        .packet(scanResult.getScanRecord().getBytes())
                        .rssi(scanResult.getRssi()).build())
                .doOnSubscribe(disposable -> startScan = disposable)
                .doOnTerminate(this::stopScan);
    }

    private ScanSettings setScanSetting(ScanMode scanMode){
        ScanSettings.Builder builder = new ScanSettings.Builder();


        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            builder = builder.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
                             .setMatchMode(ScanSettings.MATCH_MODE_AGGRESSIVE);
        }

        return builder.setScanMode(scanMode.rawValue())
                      .setReportDelay(0)
                      .build();
    }

    private List<ScanFilter> setScanFilterList(String deviceName, String macAddress) {
        List<ScanFilter> scanFilterList = new Vector<>();

        ScanFilter.Builder builder = new ScanFilter.Builder();

        if(!deviceName.equals("")){
            builder = builder.setDeviceName(deviceName);
        }

        if(!macAddress.equals("")){
            builder = builder.setDeviceAddress(macAddress);
        }

        scanFilterList.add(builder.build());

        return scanFilterList;
    }

ScanWorker.java

      ....
....
 ScanService scanService = new ScanService(context);
 scanService.startScan(deviceName, syncedMac, 40, ScanMode.BALANCED)
                    .subscribeOn(Schedulers.io())
                    .observeOn(Schedulers.io())
                    .firstOrError()
                    .onErrorResumeNext(throwable -> {
                        scanService.stopScan();
                        return throwable instanceof NoSuchElementException ? Single.error(new NotFoundException(context)) : Single.error(throwable);
                    })
                    .map(scanDeviceVo -> new AdvData(scanDeviceVo.getPacket()))
                    .subscribe(advData -> { ..send with retrofit.. });
....
....

Если возникает ошибка, она предназначена для отправки в Firebase Crashlytics (например, отключение Bluetooth, исключение разрешений и т. д.).

          public CheckStatus checkBleSupport(){
        if(bluetoothAdapter == null) {
            return CheckStatus.BLUETOOTH_UNSUPPORTED;
        }

        if(!bluetoothAdapter.isEnabled()) {   //<---- here, i desigend this logic to be sent to firebase crashlytics. this line is false sometimes, when i BLE scanning a background that works periodically.
            return CheckStatus.BLUETOOTH_OFF;
        }

        if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
            return CheckStatus.BLE_UNSUPPORTED;
        }


        return CheckStatus.BLUETOOTH_ENABLE;
    }

Я заметил, что на некоторых мобильных телефонах иногда возникают исключения Bluetooth Off. (это означает, что bluetoothAdapter.isEnabled() имеет значение false.)

возникает ошибка спецификация телефона: Android 13Galaxy S21 5G

Почему это происходит?

Я обнаружил веб-сайт для разработчиков Android. (https://developer.android.com/about/versions/13/changes/battery)

Что изменилось в Android 13, так это то, что «если вы получаете слишком много широковещательных приемников, они добавляются в корзину приложений с ограниченным доступом».

Я ввел следующую команду в терминале.

      adb shell am get-standby-bucket PACKAGE_NAME
//20

Связано ли значение резервного сегмента 20 с тем, что фон bluetoothAdapter.isEnabled() иногда имеет значение false?

Я был обеспокоен в течение нескольких дней ...

0 ответов

Другие вопросы по тегам