【问题标题】:Bluetooth Location Permissions on Android 6.0Android 6.0 上的蓝牙位置权限
【发布时间】:2017-01-26 08:36:10
【问题描述】:

首先,我想指出,我学习 Android 才几周,所以请原谅任何破旧的代码。我正在尝试制作一个应用程序,它只会列出附近的蓝牙低功耗设备(目前在日志中)并最终连接到它们。

开始这个项目几周​​后,我遇到了成功的蓝牙扫描所需的 Android 6.0 位置权限。以下代码是我目前所拥有的:

package com.benstechtips.bluetoothletest4;

import android.annotation.TargetApi;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
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.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.List;

@TargetApi(21)
public class MainActivity extends AppCompatActivity {

private BluetoothAdapter mBluetoothAdapter;
private int REQUEST_ENABLE_BT = 1;
private Handler mHandler;
private static final long SCAN_PERIOD = 10000;
private BluetoothLeScanner mLEScanner;
private ScanSettings settings;
private List<ScanFilter> filters;
private BluetoothGatt mGatt;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    mHandler = new Handler();
    if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
        Toast.makeText(this, "BLE Not Supported", Toast.LENGTH_SHORT).show();
        finish();
    }
    final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
    mBluetoothAdapter = bluetoothManager.getAdapter();


}

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

    if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
        Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
        startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
    } else {
        if (Build.VERSION.SDK_INT >= 21) {
            mLEScanner = mBluetoothAdapter.getBluetoothLeScanner();
            settings = new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build();
            filters = new ArrayList<ScanFilter>();
        }
        scanLeDevice(true);
    }
}

@Override
protected void onPause() {
    super.onPause();
    if (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled()) {
        scanLeDevice(false);
    }
}

@Override
protected void onDestroy() {
    if (mGatt == null) {
        return;
    }
    mGatt.close();
    mGatt = null;
    super.onDestroy();
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == REQUEST_ENABLE_BT) {
        if (resultCode == Activity.RESULT_CANCELED) {
            //Bluetooth not enabled.
            finish();
            return;
        }
    }
    super.onActivityResult(requestCode, resultCode, data);
}

private void scanLeDevice(final boolean enable) {
    if (enable) {
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                Log.i("Scan Status", "Stopping Scan...");
                if (Build.VERSION.SDK_INT < 21) {
                    mBluetoothAdapter.stopLeScan(mLeScanCallback);
                } else {
                    mLEScanner.stopScan(mScanCallback);

                }
            }
        }, SCAN_PERIOD);
        if (Build.VERSION.SDK_INT < 21) {
            Log.i("Scan Status", "Starting Scan...");
            mBluetoothAdapter.startLeScan(mLeScanCallback);
        } else {
            mLEScanner.startScan(filters, settings, mScanCallback);
        }
    } else {
        Log.i("Scan Status", "Stopping Scan...");
        if (Build.VERSION.SDK_INT < 21) {
            mBluetoothAdapter.stopLeScan(mLeScanCallback);
        } else {
            mLEScanner.stopScan(mScanCallback);
        }
    }
}

private ScanCallback mScanCallback = new ScanCallback() {
    @Override
    public void onScanResult(int callbackType, ScanResult result) {
        Log.i("callbackType", String.valueOf(callbackType));
        Log.i("result", result.toString());
        BluetoothDevice btDevice = result.getDevice();
        //connectToDevice(btDevice);
    }

    @Override
    public void onBatchScanResults(List<ScanResult> results) {
        for (ScanResult sr : results) {
            Log.i("ScanResult - Results", sr.toString());
        }
    }

    @Override
    public void onScanFailed(int errorCode) {
        Log.e("Scan Failed", "Error Code: " + errorCode);
    }
};

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() {
                        Log.i("onLeScan", device.toString());
                        //connectToDevice(device);
                    }
                });
            }
};

public void connectToDevice(BluetoothDevice device) {
    if (mGatt == null) {
        mGatt = device.connectGatt(this, false, gattCallback);
        scanLeDevice(false);// will stop after first device detection
    }
}

private final BluetoothGattCallback gattCallback = new BluetoothGattCallback() {
    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
        Log.i("onConnectionStateChange", "Status: " + status);
        switch (newState) {
            case BluetoothProfile.STATE_CONNECTED:
                Log.i("gattCallback", "STATE_CONNECTED");
                gatt.discoverServices();
                break;
            case BluetoothProfile.STATE_DISCONNECTED:
                Log.e("gattCallback", "STATE_DISCONNECTED");
                break;
            default:
                Log.e("gattCallback", "STATE_OTHER");
        }

    }

    @Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
        List<BluetoothGattService> services = gatt.getServices();
        Log.i("onServicesDiscovered", services.toString());
        gatt.readCharacteristic(services.get(1).getCharacteristics().get
                (0));
    }

    @Override
    public void onCharacteristicRead(BluetoothGatt gatt,
                                     BluetoothGattCharacteristic
                                             characteristic, int status) {
        Log.i("onCharacteristicRead", characteristic.toString());
        gatt.disconnect();
    }
};

现在,据我所知,在 6.0 之前的 Android 版本上,此代码可以工作并显示已发现设备的名称(我暂时禁用了连接到该设备的功能)。但是,在 Android 6.0 上,我收到以下错误:

java.lang.SecurityException: Need ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permission to get scan results

我了解这是因为我的应用没有所需的位置权限。虽然我的AndroidManifest.xml 文件中有以下内容,但我知道我需要向用户请求许可。

<?xml version="1.0" encoding="utf-8"?>

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

<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:supportsRtl="true"
    android:theme="@style/AppTheme">
    <activity android:name=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

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

但我的问题是,我如何向用户请求许可?

【问题讨论】:

    标签: android bluetooth location bluetooth-lowenergy


    【解决方案1】:

    我遇到了同样的问题!这是 Marshmallow 中最近添加的安全功能。以下是请求权限的样子:

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (this.checkSelfPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                requestPermissions(new String[]{android.Manifest.permission.ACCESS_COARSE_LOCATION},
                        PERMISSION_REQUEST_COARSE_LOCATION);
            }
    }
    

    这样

    private static final int PERMISSION_REQUEST_COARSE_LOCATION = 1;
    

    我只是选择了粗略的位置。据我所知,选择哪一个并不重要。您可以将其放在主要活动的 onCreate 中。希望这会有所帮助!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-02-10
      • 2013-09-01
      • 2021-11-26
      • 1970-01-01
      • 2021-08-15
      • 1970-01-01
      • 2022-11-11
      • 2016-01-07
      相关资源
      最近更新 更多