Googles Android/ Bluetooth

Aus Wikibooks
Zur Navigation springen Zur Suche springen

Zurück zu Googles Android

Erlaubnis (Permission)[Bearbeiten]

Wie auch bei vielen andere Systemen in Android, kann Bluetooth in einer Anwendung nur genutzt werden, wenn die entsprechenden Erlaubnisse im Manifest gesetzt wurden. Für die Nutzung von Bluetooth werden zwei Erlaubnisse gebraucht:

  • BLUETOOTH: Diese erlaubt es die grundlegendsten Funktionen der Bluetooth-Kommunikation zu nutzen. Darunter fallen das Anfragen einer Verbindung, das Akzeptieren einer eingehenden Verbindung und die Datenübertragung.
  • BLUETOOTH_ADMIN: Durch diese ist es möglich, eine Suche nach anderen Bluetooth-Geräten zu starten oder die Bluetooth-Einstellungen zu verändern.
<manifest ... >
  ...
  <uses-permission android:name="android.permission.BLUETOOTH" />
  <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
  ...
</manifest>

Sind Sie da?[Bearbeiten]

Auch wenn man davon ausgehen kann, dass die allermeisten (Android-)Smartphones über Bluetooth verfügen, sollte man jedoch als guter Entwickler immer testen, ob ein zu benutzender Dienst auch existiert. Das in Android zu überprüfen, ist simpel ab API-Level 5:

BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter == null) {
    // Das Gerät verfügt über kein Bluetooth.
}

Um zu testen, ob das Smartphone über Bluetooth verfügt, holen wir uns vom System den BluetoothAdapter[1] über die Methode getDefaultAdapter().[2] Dieser BluetoothAdapter stellt die Repräsentation des eigenen Bluetooth-Gerätes dar. Wenn man als Rückgabewert ein NULL erhält, besitzt das Gerät kein Bluetooth.

Wenn das Gerät über Bluetooth verfügt, muss sichergestellt werden, dass es auch zum Zeitpunkt der Nutzung aktiviert ist. Die Aktivierung muss aber nochmals explizit durch den Nutzer bestätigt werden.

if (!mBluetoothAdapter.isEnabled()) {
    Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
    startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}

Wenn Bluetooth noch nicht aktiv ist, dann wird ein Intent mit der BluetoothAdapter.ACTION_REQUEST_ENABLE-Aktion erzeugt, welche per startActivityForResult()[3]-Methode ins System abgesetzt wird. Dadurch öffnet sich ein Dialog, welcher den Nutzer fragt, ob Bluetooth aktiviert werden soll oder nicht. Durch startActivityForResult() wird nach Abarbeitung des Intents bzw. die Antwort des Nutzers, die Callback-Methode onActivityResult()[4] aufgerufen. Dieser ResultCode muss allerdings zunächst definiert werden. [5]

private final static int REQUEST_ENABLE_BT = 123;

Durch Auslesen des ResultCodes kann abgelesen werden, ob die Aktivierung erfolgreich war oder nicht.

public void onActivityResult(int requestCode, int resultCode, Intent data) {
   if (resultCode == Activity.RESULT_OK) {
      // Bluetooth ist aktiv.
   } else {
      // Bluetooth wurde nicht aktiviert.
   }
}

Andere Geräte aufspüren[Bearbeiten]

Um eine Verbindung zu einem anderen Bluetooth-Gerät herzustellen, ist es zunächst notwendig, dass man nach allen in der Nähe befindlichen Geräten sucht und von diesen Informationen abruft. Diese Informationen beinhalten den Gerätenamen, dessen Klasse und seine MAC-Adresse. Anhand dieser Informationen ist es dann möglich, sich mit einem anderen Gerät zu „paaren“ (vom engl. Begriff pairing), das heißt diese Geräte tauschen zum Zweck der Authentifizierung und Verschlüsselung einen Schlüssel aus. Alle mit dem eigenen Gerät gepaarten Geräte werden gespeichert und können durch die Bluetooth-API jederzeit abgerufen werden. So kann man anhand der MAC-Adresse jederzeit eine Verbindung zu einem Gerät herstellen ohne eine neue Suche anstoßen zu müssen. Voraussetzung dafür ist natürlich, dass sich das gewünschte Gerät in Reichweite befindet.

Gepaarte Geräte[Bearbeiten]

Wenn man eine Verbindung zu einem bereits bekannten Gerät aufbauen möchte, muss man keine neue Suche nach anderen Geräten starten, da man alle Informationen auf dem eigenen Gerät finden kann.

Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
// wenn gepaarte Geräte existieren
if (pairedDevices.size() > 0) {
    // Durchgehen der gepaarten Geräte
    for (BluetoothDevice device : pairedDevices) {
        	// einem Array die Addresse und den Namen der Geräte hinzufügen
        mArrayAdapter.add(device.getName() + "\n" + device.getAddress());
    }
}

Durch die Methode getBondedDevices()[6] wird die Liste aller gespeicherten und gepaarten Geräte abgerufen.

Neue Geräte suchen[Bearbeiten]

Wenn man eine Verbindung zu einem dem eigenem Gerät noch unbekanntem Gerät herstellen will, muss man zunächst nach diesem Gerät suchen. Dies erreicht man durch den Aufruf der Methode startDiscovery().[7] Der Aufruf dieser Methode löst eine asynchrone Suche aus. Nach Beendigung dieser Suche, wird durch das Aussenden der Aktion ACTION_FOUND in das System das Ergebnis dem System zugänglich gemacht und kann abgefragt werden. Zu diesem Zweck benötigt man einen BroadcastReceiver,[8] der auf die Beendigung der Suche reagiert.

// BroadcastReceiver für ACTION_FOUND
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        // wenn durch die Suche ein Gerät gefunden wurde
        if (BluetoothDevice.ACTION_FOUND.equals(action)) {
	    // das Bluetooth-Gerät aus dem Intent holen
            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
            // Hinzufügen des Namens und der Adresse in ein Array
            mArrayAdapter.add(device.getName() + "\n" + device.getAddress());
        }
    }
};

// den BroadcastReceiver im System registrieren
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(mReceiver, filter);

Sich sichtbar machen[Bearbeiten]

Um sein Gerät für andere sichtbar zu machen einfach den folgenden Code kopieren.[9]

Intent sichtbarMachen = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
startActivity(sichtbarMachen);

Geräte verbinden[Bearbeiten]

Um die Verbindung zwischen zwei Geräten herzustellen, benötigt eine Anwendung jeweils eine Server- und Client-Implementierung. Wie bei diesem Modell üblich öffnet der Server eine Verbindung und wartet, bis diese von einem Client genutzt wird. Ziel beider Seiten ist es, einen BluetoothSocket[10] zu öffnen, auf welchem Daten zwischen den beiden Seiten übertragen werden können.

Server[Bearbeiten]

Um auf dem Server die Verbindung zustande zu bringen, sind folgende Schritte zu beachten:

1. Durch den Aufruf der Methode listenUsingRfcommWithServiceRecord(String, UUID)[11] wird ein BluetoothServerSocket[12] geöffnet. listenUsingRfcommWithServiceRecord() schreibt einen Eintrag ins Service Discovery Protocol (SDP). Dabei wird ein Name und eine UUID für den eigenen Service benötigt. Dieser Name ist beliebig. Die UUID muss auf der Server- und der Client-Seite identisch sein, damit eine Verbindung zustande kommen kann.

2. Auf eine eingehende Verbindung warten

Durch den Aufruf von accept()[13] wartet der Server auf eine eingehende Verbindung und gibt ein BluetoothSocket-Objekt zurück. Der Aufruf von accept() blockiert den Thread bis zu dieser Verbindung, weshalb er nicht im UI-Thread der Anwendung laufen sollte.

3. Schließen des BluetoothServerSockets

Nach dem Erhalt des BluetoothSockets muss der BluetoothServerSocket durch den Aufruf von close()[14] geschlossen werden. Dies gibt den BluetoothServerSocket und all seine Ressourcen frei, schließt dabei aber nicht den BluetoothSocket. Dieser kann weiter für die Datenübertragung genutzt werden.

Beispiel-Implementierung für die Server-Seite:

private class AcceptThread extends Thread {
    private final BluetoothServerSocket mmServerSocket;

    public AcceptThread() {
        // Use a temporary object that is later assigned to mmServerSocket,
        // because mmServerSocket is final.
        BluetoothServerSocket tmp = null;
        try {
            // MY_UUID is the app’s UUID string, also used by the client code.
            tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
        } catch (IOException e) { }
        mmServerSocket = tmp;
    }

    public void run() {
        BluetoothSocket socket = null;
        // Keep listening until exception occurs or a socket is returned.
        while (true) {
            try {
                socket = mmServerSocket.accept();
            } catch (IOException e) {
                break;
            }
            // if a connection was accepted
            if (socket != null) {
                // Do work to manage the connection (in a separate thread).
                manageConnectedSocket(socket);
                mmServerSocket.close();
                break;
            }
        }
    }

    /** Will cancel the listening socket, and cause the thread to finish. */
    public void cancel() {
        try {
            mmServerSocket.close();
        } catch (IOException e) { }
    }
}

Client[Bearbeiten]

Für die Verbindung zum Server muss der Client folgende Schritte durchlaufen:

1. Erstellen eines BluetoothSockets zum gewählten Gerät

Durch den Aufruf von createRfcommSocketToServiceRecord(UUID) auf dem gewählten Gerät, erhalten wir ein BluetoothSocket-Objekt. Die verwendete UUID muss dieselbe sein wie auf der Server-Seite.

2. Verbindung aufbauen

Durch den Aufruf von connect() auf dem BluetoothSocket versucht das System, eine Verbindung zum Socket auf dem Server herzustellen. Ist dies von Erfolg gekrönt, kann zwischen beiden eine Kommunikation stattfinden.


Beispiel-Implementierung für die Client-Seite:

private class ConnectThread extends Thread {
    private final BluetoothSocket mmSocket;
    private final BluetoothDevice mmDevice;

    public ConnectThread(BluetoothDevice device) {
        // Use a temporary object that is later assigned to mmSocket,
        // because mmSocket is final.
        BluetoothSocket tmp = null;
        mmDevice = device;

        // Get a BluetoothSocket to connect with the given BluetoothDevice.
        try {
            // MY_UUID is the app’s UUID string, also used by the server code.
            tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
        } catch (IOException e) { }
        mmSocket = tmp;
    }

    public void run() {
        // Cancel discovery because it will slow down the connection.
        mAdapter.cancelDiscovery();

        try {
            // Connect the device through the socket. This will block
            // until it succeeds or throws an exception.
            mmSocket.connect();
        } catch (IOException connectException) {
            // Unable to connect; close the socket and get out.
            try {
                mmSocket.close();
            } catch (IOException closeException) { }
            return;
        }

        // Do work to manage the connection (in a separate thread).
        manageConnectedSocket(mmSocket);
    }

    /** Will cancel an in-progress connection, and close the socket. */
    public void cancel() {
        try {
            mmSocket.close();
        } catch (IOException e) { }
    }
}

Daten-Handhabung[Bearbeiten]

Der Aufbau einer Verbindung zwischen zwei Bluetooth-Geräten ist nur die eine Seite der Medaille. Nach dem Aufbau dieser Verbindung muss dann auch noch der Austausch der Daten bewältigt werden. Bei der Übertragung der Daten kommen Input- und Outputstreams zum Einsatz.

Beispiel-Implementierung für die Handhabung der Daten:

private class ConnectedThread extends Thread {
    private final BluetoothSocket mmSocket;
    private final InputStream mmInStream;
    private final OutputStream mmOutStream;

    public ConnectedThread(BluetoothSocket socket) {
        mmSocket = socket;
        InputStream tmpIn = null;
        OutputStream tmpOut = null;

        // Get the input and output streams, using temp objects because
        // member streams are final.
        try {
            tmpIn = socket.getInputStream();
            tmpOut = socket.getOutputStream();
        } catch (IOException e) { }

        mmInStream = tmpIn;
        mmOutStream = tmpOut;
    }

    public void run() {
        byte[] buffer = new byte[1024];  // buffer store for the stream
        int bytes; // bytes returned from read()

        // Keep listening to the InputStream until an exception occurs.
        while (true) {
            try {
                // Read from the InputStream
                bytes = mmInStream.read(buffer);
                // Send the obtained bytes to the UI Activity.
                mHandler.obtainMessage(MESSAGE_READ, bytes, -1, buffer)
                        .sendToTarget();
            } catch (IOException e) {
                break;
            }
        }
    }

    /* Call this from the main Activity to send data to the remote device. */
    public void write(byte[] bytes) {
        try {
            mmOutStream.write(bytes);
        } catch (IOException e) { }
    }

    /* Call this from the main Activity to shutdown the connection. */
    public void cancel() {
        try {
            mmSocket.close();
        } catch (IOException e) { }
    }
}

Einzelnachweise[Bearbeiten]

  1. BluetoothAdapter: http://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html. Stand 24. Februar 2011.
  2. http://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#getDefaultAdapter%28%29. Stand 24. Februar 2011.
  3. http://developer.android.com/reference/android/app/Activity.html#startActivityForResult%28android.content.Intent,%20int%29. Stand 24. Februar 2011.
  4. http://developer.android.com/reference/android/app/Activity.html#startActivityForResult%28android.content.Intent,%20int%29. Stand 24. Februar 2011.
  5. http://stackoverflow.com/questions/8188277/error-checking-if-bluetooth-is-enabled-in-android-request-enable-bt-cannot-be-r
  6. http://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#getBondedDevices%28%29, Stand 25. Februar 2011.
  7. http://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#startDiscovery%28%29. Stand 25. Februar 2011.
  8. BroadcastReceiver: http://developer.android.com/reference/android/content/BroadcastReceiver.html. Stand 25. Februar 2011.
  9. https://androidcookbook.com/Recipe.seam?recipeId=1978&recipeFrom=home Stand 03. März 2016.
  10. BluetoothSocket:http://developer.android.com/reference/android/bluetooth/BluetoothSocket.html, Stand 25. Februar 2011.
  11. http://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#listenUsingRfcommWithServiceRecord%28java.lang.String,%20java.util.UUID%29. Stand 25. Februar 2011.
  12. BluetoothServerSocket: http://developer.android.com/reference/android/bluetooth/BluetoothServerSocket.html. Stand 25. Februar 2011.
  13. http://developer.android.com/reference/android/bluetooth/BluetoothServerSocket.html#accept%28%29. Stand 25. Feb. 2011
  14. http://developer.android.com/reference/android/bluetooth/BluetoothServerSocket.html#close%28%29. Stand 25. Februar 2011.