android 6.0 中无需位置许可的信标扫描

Beacon scanning without location permission in android 6.0

大家好,我正在开发基于 android 的 beacon 应用程序,它在 Android 6.0 以下工作正常,它只需要蓝牙许可。但是Android 6.0及以上版本要求提供蓝牙和位置权限。

为什么在Android6.0中扫描信标需要位置权限?

提前致谢!!!

我实际上正在开发类似的东西,这也让我感到惊讶,因为从逻辑上讲,您不需要位置许可即可通过蓝牙/低功耗扫描信标。这实际上适用于 Android 6.0 之前的版本 - 但 Google 决定改变它。

From the notes:

Access to Hardware Identifier To provide users with greater data protection, starting in this release, Android removes programmatic access to the device’s local hardware identifier for apps using the Wi-Fi and Bluetooth APIs. The WifiInfo.getMacAddress() and the BluetoothAdapter.getAddress() methods now return a constant value of 02:00:00:00:00:00.

To access the hardware identifiers of nearby external devices via Bluetooth and Wi-Fi scans, your app must now have the ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permissions:

WifiManager.getScanResults()
BluetoothDevice.ACTION_FOUND
BluetoothLeScanner.startScan()

Note: When a device running Android 6.0 (API level 23) initiates a background Wi-Fi or Bluetooth scan, the operation is visible to external devices as originating from a randomized MAC address

这并不意味着您必须打开 GPS,而是 Bluetooh / Battery Saving 的定位模式请看这里的图片

我检查了我很久以前做的代码,不,你只需要启用蓝牙。正如我在我的代码中看到的那样。请忽略 postNotification 方法,因为它不是必需的。

private void initBluetooth() {
        // Check if device supports Bluetooth Low Energy.
        if (!beaconManager.hasBluetooth()) {
            //Toast.makeText(this, "Device does not have Bluetooth Low Energy", Toast.LENGTH_LONG).show();
            return;
        }

        // If Bluetooth is not enabled, let user enable it.
        if (!beaconManager.isBluetoothEnabled()) {
            Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(enableBtIntent, Constant.REQUEST_ENABLE_BT);
        } else {
            Log.v(this.getClass(), "---> bluetooth already is enabled");
            startService(new Intent(this, EstimoteService.class));
        }
    }

//EstimoteServiceclass

public class EstimoteService extends Service implements BeaconManager.RangingListener, BeaconManager.MonitoringListener {
    private static final int NOTIFICATION_ID = 123;
    private static final Region ALL_ESTIMOTE_BEACONS_REGION = new Region("regionId", null, null, null);
    private Beacon beaconFound;

    private NotificationManager notificationManager;
    private BeaconManager beaconManager;

    @Override
    public void onCreate() {
        super.onCreate();

        initResources();
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        notificationManager.cancel(NOTIFICATION_ID);

        // Default values are 5s of scanning and 25s of waiting time to save CPU cycles.
        // In order for this demo to be more responsive and immediate we lower down those values.
        beaconManager.setBackgroundScanPeriod(TimeUnit.SECONDS.toMillis(1), 0);
        beaconManager.connect(new BeaconManager.ServiceReadyCallback() {
            @Override
            public void onServiceReady() {
                try {
                    beaconManager.startRanging(ALL_ESTIMOTE_BEACONS_REGION);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });


        return START_NOT_STICKY;
    }

    private void initResources() {
        notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        beaconManager = new BeaconManager(this);
        beaconManager.setRangingListener(this);
        beaconManager.setMonitoringListener(this);
    }

    @Override
    public void onBeaconsDiscovered(Region region, List<Beacon> beacons) {
        if (beaconFound == null) {
            // Get the first available beaconFound
            if (beacons.isEmpty())
                return;

            beaconFound = beacons.get(0);
            final Region beaconRegion = new Region("regionId", beaconFound.getProximityUUID(), beaconFound.getMajor(), beaconFound.getMinor());
            beaconManager.connect(new BeaconManager.ServiceReadyCallback() {
                @Override
                public void onServiceReady() {
                    try {
                        beaconManager.startMonitoring(beaconRegion);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }

    @Override
    public void onEnteredRegion(Region region, List<Beacon> beacons) {
        postNotification("Entered region");
        System.out.println("---> enter region");
    }

    @Override
    public void onExitedRegion(Region region) {
        postNotification("Exited region");
        System.out.println("---> exit region");
    }

    private void postNotification(String msg) {
        NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
        builder.setSmallIcon(R.drawable.beacon_gray).setContentTitle("XXX").setContentText(msg);
        notificationManager.notify(NOTIFICATION_ID, builder.build());
    }
}