2016-07-01 10 views
4

Ich versuche, eine Verbindung zu einem Bluetooth-Gerät auf Android herzustellen. Ich erhalte den Status 133 in meinem Handler onClientConnectionState. Ich bekomme diesen Fehler nicht immer - manchmal verbindet es sich gut. Ich bin nicht in der Lage gewesen zu sagen, was das Problem auslöst. Ich hatte es sogar sofort nach dem Neustart des Geräts und meiner Repro-App.Android Bluetooth Fehler 133

Ich bin mir dessen bewusst einige Fragen und Lösungsvorschläge für dieses Problem, einschließlich (von here, here und here):

  • den UI-Thread für alle BT-APIs verwenden
  • sicher sein, schließen das GATT wenn fertig

Aber ich mache all das. Außerdem ist mein Gerät ein Nexus 5 (läuft Lollipop), das nach einigen nicht einmal die BT-Interaktionen benötigt, um auf dem UI-Thread zu sein.

Ich habe eine möglichst einfache Repro erstellt. Es ist in C#, aber die Java-Äquivalent sollte klar sein:

[Activity(Label = "BluetoothGatt133ErrorRepro", MainLauncher = true, Icon = "@drawable/icon")] 
public class MainActivity : Activity 
{ 
    protected override void OnCreate(Android.OS.Bundle bundle) 
    { 
     base.OnCreate(bundle); 

     SetContentView(Resource.Layout.Main); 
     var button = FindViewById<Button>(Resource.Id.button); 
     button.Click += this.OnClick; 
    } 

    private async void OnClick(object sender, EventArgs e) 
    { 
     Action<string> log = message => Console.WriteLine($"***** #{Environment.CurrentManagedThreadId} {message}"); 

     log("Beginning"); 

     var bluetoothManager = (BluetoothManager)Application.Context.GetSystemService(Context.BluetoothService); 
     var adapter = bluetoothManager.Adapter; 
     var scanner = adapter.BluetoothLeScanner; 
     var callback = new Callback(); 
     var filters = new List<ScanFilter>(); 
     var settings = new ScanSettings.Builder() 
      .SetScanMode(global::Android.Bluetooth.LE.ScanMode.LowLatency) 
      .Build(); 

     log("Starting scan"); 
     scanner.StartScan(filters, settings, callback); 

     var result = await callback.Result; 
     log($"Got device: {result.Device.Name}"); 

     var remoteDevice = adapter.GetRemoteDevice(result.Device.Address); 
     var gattCallback = new GattCallback(log); 

     log("Connecting GATT"); 

     var gatt = remoteDevice.ConnectGatt(Application.Context, true, gattCallback); 
     gatt.Connect(); 

     await gattCallback.Result; 

     log("Disconnecting GATT"); 

     gatt.Close(); 
     gatt.Dispose(); 
    } 

    private sealed class Callback : ScanCallback 
    { 
     private readonly TaskCompletionSource<ScanResult> result; 

     public Callback() 
     { 
      this.result = new TaskCompletionSource<ScanResult>(); 
     } 

     public Task<ScanResult> Result => this.result.Task; 

     public override void OnBatchScanResults(IList<ScanResult> results) 
     { 
      foreach (var result in results) 
      { 
       this.HandleResult(result); 
      } 
     } 

     public override void OnScanResult(ScanCallbackType callbackType, ScanResult result) 
     { 
      this.HandleResult(result); 
     } 

     public override void OnScanFailed(ScanFailure errorCode) 
     { 
      this.result.TrySetException(new InvalidOperationException($"Failed with error code {errorCode}.")); 
     } 

     private void HandleResult(ScanResult result) 
     { 
      if (result.Device.Name.Contains("elided")) 
      { 
       this.result.TrySetResult(result); 
      } 
     } 
    } 

    private sealed class GattCallback : BluetoothGattCallback 
    { 
     private readonly Action<string> log; 
     private readonly TaskCompletionSource<bool> result; 

     public GattCallback(Action<string> log) 
     { 
      this.log = log; 
      this.result = new TaskCompletionSource<bool>(); 
     } 

     public Task<bool> Result => this.result.Task; 

     public override void OnConnectionStateChange(BluetoothGatt gatt, GattStatus status, ProfileState newState) 
     { 
      this.log($"Connection state changed to {newState} with status {status}."); 

      this.result.TrySetResult(true); 
     } 
    } 
} 

Und hier ist die Ausgabe von diesem ausgeführt wird (ich habe auch von Android BluetoothGatt Quelle in den Ausgang links):

***** #1 Beginning 
***** #1 Starting scan 
07-01 11:53:21.458 D/BluetoothLeScanner(10377): onClientRegistered() - status=0 clientIf=5 
***** #1 Got device: elided 
***** #1 Connecting GATT 
07-01 11:53:22.833 D/BluetoothGatt(10377): connect() - device: 00:00:DE:AD:BE:EF, auto: true 
07-01 11:53:22.833 D/BluetoothGatt(10377): registerApp() 
07-01 11:53:22.833 D/BluetoothGatt(10377): registerApp() - UUID=fa5bce8a-416d-47fe-9a8a-e44156f7e865 
07-01 11:53:22.834 D/BluetoothGatt(10377): onClientRegistered() - status=0 clientIf=6 
07-01 11:53:24.622 D/BluetoothGatt(10377): onClientConnectionState() - status=133 clientIf=6 device=00:00:DE:AD:BE:EF 
***** #4 Connection state changed to Disconnected with status 133. 
***** #1 Disconnecting GATT 
07-01 11:53:24.707 D/BluetoothGatt(10377): close() 
07-01 11:53:24.707 D/BluetoothGatt(10377): unregisterApp() - mClientIf=6 

Wie Sie können Sehen Sie, meine gesamte Interaktion mit dem Bluetooth-Stack findet im Hauptthread (# 1) statt. Aber trotzdem empfange ich den Status 133 in meinem onClientConnectionState Handler.

Mein Manifest hat diese Berechtigungen:

<uses-permission android:name="android.permission.BLUETOOTH" /> 
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> 
    <uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED" /> 
    <uses-permission android:name="android.permission.INTERNET" /> 
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> 
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> 

ich mit dem neuesten Eibisch Tooling bin kompilieren und bin Targeting Eibisch mit einem Mindestziel von 4.0.3 (API-Ebene 15).

Was könnte das verursachen?

+1

Können Sie uns sagen, welche Berechtigungen Sie in Ihrer Manifestdatei haben? Könnten Sie auch versuchen, den Scan zu stoppen, bevor Sie die Verbindung herstellen? – Zomb

+0

^^ upvoted für den Scan-Vorschlag zu stoppen, hat nicht bemerkt, dass – Submersed

+0

@Zomb Habe mehr Details zu meiner Frage hinzugefügt. Es funktioniert anscheinend heute Morgen gut für mich, aber ich muss versuchen, den Scan-Trick zu stoppen, wenn das nächste Mal auftaucht. –

Antwort

2

(Hinweis: Sie können dies bereits tun, aber ich bin nicht gut in C# versiert)

Nach meiner Erfahrung ist es nicht wirklich gewesen, dass Sie nur mit dem BLE Gerät auf dem Haupt-Thread in Wechselwirkung treten Es ist nur so, dass Sie das Gerät nicht mit zu vielen Anfragen gleichzeitig überfluten.

Ich habe dieses Problem bei der Arbeit mit BLE auf Android (und lesen Sie ähnliche Kommentare über die Verwendung des Hauptthreads), und es war, weil ich zu viele Anfragen posten (lesen/schreiben, Benachrichtigung/Anzeige Registrierungen, etc) zu dem entfernten Gatt-Gerät, bevor Rückrufe für vorherige Operationen in dem BluetoothGattCallback-Objekt empfangen werden. Ich errichte meine eigene gemanagte Gatt-Operationswarteschlange (ein Thread, der blockiert, bis ein Rückruf für diese Operation in GattCallback empfangen wird, oder die anfängliche Lese-/Schreiboperation gibt false zurück und behandelt dann die nächste in der Warteschlange befindliche Operation oder Wiederholungen mit einem Backoff-Multiplikator). und da ich nicht auf dieses Problem gestoßen bin. Soweit ich das beurteilen kann, macht Android keine guten "Warteschlangen" -Operationen, daher beißt der Boolesche "isBusy" Sie unwissentlich (schauen Sie sich die BlueoothGattCharacteristic write Methode an, wenn diese nicht auf Sie hinausspringt). Ich habe auch bemerkt, dass Sie in Ihrem Callback-Objekt nicht viel arbeiten wollen, aber den Callback an einen anderen Thread delegieren oder das Ergebnis ausstrahlen (damit Sie den Binder-Thread nicht blockieren). Normalerweise kopiere ich einfach die Byte-Payload und gebe sie an einen anderen HandlerThread weiter, um ihn zu parsen.

Auch, ja, trennen und schließen ist ziemlich sicher.Ich verwende normalerweise einen Service, um BLE-Interaktionen zu behandeln und beide aufzurufen, bevor der onDestroy des Service beendet wird.

+0

Ich habe bereits eine Operations-Warteschlange in meiner realen Implementierung, aber wie Sie in meiner Test-App oben sehen können, komme ich nicht einmal so weit. Ich scheitere (oft, aber nicht immer) an der ersten Hürde, die Verbindung herzustellen. –

+0

Auch, ist das immer das Ergebnis, das du bekommst, oder hast du jemals eine erfolgreiche Verbindung bekommen? – Submersed

+0

Entschuldigung, nahm mich eine Sekunde. Interessant. Welche Firmware? – Submersed