20

Poniższy kod działa doskonale na moim Nexusie 9 z systemem Android 5.1.1 (kompilacja LMY48M), ale nie działa na Nexusie 9 z systemem Android 6.0 (kompilacja MPA44l)Funkcja Bluetooth LE ScanFilters nie działa na systemie Android M

List<ScanFilter> filters = new ArrayList<ScanFilter>(); 
ScanSettings settings = (new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_POWER)).build(); 
ScanFilter.Builder builder = new ScanFilter.Builder(); 
builder.setManufacturerData((int) 0x0118, new byte[]{(byte) 0xbe, (byte) 0xac}, new byte[]{(byte) 0xff, (byte)0xff}); 
ScanFilter scanFilter = builder.build(); 
filters.add(scanFilter); 
mBluetoothLeScanner.startScan(filters, settings, new ScanCallback() { 
    ... 
}); 

W systemie Android 5.x powyższy kod powoduje wywołanie zwrotne, gdy wyświetlana jest reklama producenta pasująca do filtru skanowania. (Zobacz przykładowy wynik Logcat poniżej.) Na Nexusie 9 z MPA44l nie są odbierane żadne oddzwanianie. Jeśli wykomentuj filtr skanowania callbacks odbierane są z powodzeniem na Nexusa 9.

09-22 00:07:28.050 1748-1796/org.altbeacon.beaconreference D/BluetoothLeScanner﹕ onScanResult() - ScanResult{mDevice=00:07:80:03:89:8C, mScanRecord=ScanRecord [mAdvertiseFlags=6, mServiceUuids=null, mManufacturerSpecificData={280=[-66, -84, 47, 35, 68, 84, -49, 109, 74, 15, -83, -14, -12, -111, 27, -87, -1, -90, 0, 1, 0, 1, -66, 0]}, mServiceData={}, mTxPowerLevel=-2147483648, mDeviceName=null], mRssi=-64, mTimestampNanos=61272522487278} 

Czy ktoś widział ScanFilters pracować na Androida M?

+0

Potrzebujesz obu - NETWORK_PROVIDER i GPS_PROVIDER? Lub po prostu NETWORK_PROVIDER. Więcej informacji tutaj: https://developer.android.com/guide/topics/connectivity/bluetooth-le.html –

Odpowiedz

16

Problem nie był filtrem skanowania, ale filtr skanowania był używany tylko wtedy, gdy aplikacja była w tle. Począwszy od Androida M, Bluetooth LE skanowania w tle jest zablokowany, chyba że aplikacja ma jeden z dwóch następujących uprawnień:

android.permission.ACCESS_COARSE_LOCATION 
android.permission.ACCESS_FINE_LOCATION 

Aplikacja miałem badania nie poprosić któregoś z tych uprawnień, tak to nie działa w tło (jedyny czas, kiedy filtr skanowania był aktywny) na Androidzie M. Dodanie pierwszego rozwiązało problem.

zdałem sobie sprawę, było to problemem, ponieważ widziałem następujący wiersz w logcat:

09-22 22:35:20.152 5158 5254 E BluetoothUtils: Permission denial: Need ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permission to get scan results 

zobacz tutaj szczegóły: https://code.google.com/p/android-developer-preview/issues/detail?id=2964

+11

Dodałem do mojego manifestu, ale ja pojawia się komunikat o błędzie: "java.lang.SecurityException: Wymagane jest uprawnienie ACCESS_COARSE_LOCATION lub ACCESS_FINE_LOCATION do uzyskania wyników skanowania". Każdy pomysł, jak to rozwiązać? –

+5

Począwszy od systemu Android 6, musisz również przedstawić prośbę o uprawnienia do usr w czasie wykonywania. Zobacz tutaj na przykład: http://developer.radiusnetworks.com/2015/09/29/is-your-beacon-app-ready-for-android-6.html – davidgyoung

+0

@davidgyoung kiedy daję pozwolenie, przestaje skanować beacons. Pomóż nam wydostać się z tego problemu. –

27

miałem podobny problem z aplikacją łączącą do Bluetooth. Nie LE ScanFilter, ale był to problem z uprawnieniami, podobnie jak OP.

Główną przyczyną jest to, że począwszy od SDK 23, musisz poprosić użytkownika o uprawnienia w czasie wykonywania, używając metody Activity 's requestPermissions().

Oto co pracował dla mnie:

  1. Dodaj jedno z następujących dwóch linii do AndroidManifest.xml wewnątrz węzła głównego:

    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> 
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> 
    
  2. W swojej działalności, przed przystąpieniem do podłączenia do bluetooth, zadzwoń pod Activity z metodą requestPermissions(), która otwiera okno dialogowe systemu, aby wyświetlić monit o zezwolenie dla użytkownika. Okno uprawnień otwiera się w innym wątku, więc pamiętaj, aby poczekać na wynik przed próbą połączenia z bluetooth.

  3. Przejęcie Activity 's onRequestPermissionsResult() do obsługi wyniku. Ta metoda naprawdę będzie musiała zrobić coś tylko wtedy, gdy użytkownik odmówi udzielenia pozwolenia, aby poinformować użytkownika, że ​​aplikacja nie może wykonać czynności bluetooth.

This blog post ma jakiś przykładowy kod, który używa AlertDialogs aby poinformować użytkownika, co się dzieje.Jest to dobry punkt wyjścia, ale ma pewne wady:

  • To nie obsługuje czeka na wątku requestPermissions() skończyć
  • The AlertDialog owijając wezwanie do requestPermissions() wydaje mi obca. Wystarczy połączenie z numerem requestPermissions().
6

Dodaj zgody lokalizacji wraz z BLE

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

Kopiuj Wklej ten sposób do prośby i lokalizacji udzielić zgody

@Override 
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { 
     switch (requestCode) { 
      case REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS: { 
       Map<String, Integer> perms = new HashMap<String, Integer>(); 
       // Initial 
       perms.put(Manifest.permission.ACCESS_FINE_LOCATION, PackageManager.PERMISSION_GRANTED); 


       // Fill with results 
       for (int i = 0; i < permissions.length; i++) 
        perms.put(permissions[i], grantResults[i]); 

       // Check for ACCESS_FINE_LOCATION 
       if (perms.get(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED 

         ) { 
        // All Permissions Granted 

        // Permission Denied 
        Toast.makeText(ScanningActivity.this, "All Permission GRANTED !! Thank You :)", Toast.LENGTH_SHORT) 
          .show(); 


       } else { 
        // Permission Denied 
        Toast.makeText(ScanningActivity.this, "One or More Permissions are DENIED Exiting App :(", Toast.LENGTH_SHORT) 
          .show(); 

        finish(); 
       } 
      } 
      break; 
      default: 
       super.onRequestPermissionsResult(requestCode, permissions, grantResults); 
     } 
    } 


    @TargetApi(Build.VERSION_CODES.M) 
    private void fuckMarshMallow() { 
     List<String> permissionsNeeded = new ArrayList<String>(); 

     final List<String> permissionsList = new ArrayList<String>(); 
     if (!addPermission(permissionsList, Manifest.permission.ACCESS_FINE_LOCATION)) 
      permissionsNeeded.add("Show Location"); 

     if (permissionsList.size() > 0) { 
      if (permissionsNeeded.size() > 0) { 

       // Need Rationale 
       String message = "App need access to " + permissionsNeeded.get(0); 

       for (int i = 1; i < permissionsNeeded.size(); i++) 
        message = message + ", " + permissionsNeeded.get(i); 

       showMessageOKCancel(message, 
         new DialogInterface.OnClickListener() { 

          @Override 
          public void onClick(DialogInterface dialog, int which) { 
           requestPermissions(permissionsList.toArray(new String[permissionsList.size()]), 
             REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS); 
          } 
         }); 
       return; 
      } 
      requestPermissions(permissionsList.toArray(new String[permissionsList.size()]), 
        REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS); 
      return; 
     } 

     Toast.makeText(ScanningActivity.this, "No new Permission Required- Launching App .You are Awesome!!", Toast.LENGTH_SHORT) 
       .show(); 
    } 

    private void showMessageOKCancel(String message, DialogInterface.OnClickListener okListener) { 
     new AlertDialog.Builder(ScanningActivity.this) 
       .setMessage(message) 
       .setPositiveButton("OK", okListener) 
       .setNegativeButton("Cancel", null) 
       .create() 
       .show(); 
    } 

    @TargetApi(Build.VERSION_CODES.M) 
    private boolean addPermission(List<String> permissionsList, String permission) { 

     if (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) { 
      permissionsList.add(permission); 
      // Check for Rationale Option 
      if (!shouldShowRequestPermissionRationale(permission)) 
       return false; 
     } 
     return true; 
    } 

a potem w onCreate czeku za zgodą

if (Build.VERSION.SDK_INT >= 23) { 
      // Marshmallow+ Permission APIs 
      fuckMarshMallow(); 
     } 

Mam nadzieję, że zaoszczędzisz swój czas.

+1

To zadziałało! Nazwa funkcji: p –

Powiązane problemy