2012-10-10 5 views
22

Ich arbeite in einem Projekt, wo ich über Bluetooth mit einem Drucker verbinden muss. Der Druckerhersteller gibt an, dass nur Android-Telefone mit SPP (Serial Port Profile) eine Verbindung mit dem Drucker herstellen können.Sollte ich den Bluetooth-Reflektionshack im Produktionscode belassen?

Dies ist, wie ich die Verbindung zunächst geöffnet:

 UUID uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); //SPP long UUID 
     BluetoothSocket socket = device.createRfcommSocketToServiceRecord(uuid); 

UUIDs verwenden die einzige Möglichkeit ist, RFCOMM-Verbindungen mit Android öffentliche API wie von JellyBean zu öffnen. Nachdem ich zuvor mit SPP-Verbindungen in BlackBerry und JavaME gearbeitet habe, wo UUIDs nicht benötigt werden, fand ich das ein bisschen seltsam. Bei UUIDs handelt es sich um Service Discovery, dh über SDP werden die im Gerät vorhandenen Dienste abgefragt. Ich muss nicht wirklich eine Entdeckung einleiten, da ich meinen Drucker im Voraus gepaart habe, und ich weiß, dass es SPP unterstützt. Genau das ist aber die Methode BluetoothDevice.createRfcommSocketToServiceRecord und die unsichere Version. Dies ist der SPP-Stack, wo wir sehen können, wie SDP ein anderes Protokoll auf der gleichen Schicht ist, und somit sollte es möglich sein RFCOMM zu verwenden, ohne eine Entdeckung zu initiieren erste:

 ----------------------------------- 
     |  My Application   | 
     ----------------------------------- 
     |  Serial Port Emulation  | 
     |   or other API   | 
     ----------------------------------- 
     |  RFCOMM   | SDP | 
     ----------------------------------- 
     | LMP | L2PCAP    | 
     ----------------------------------- 
     |   Baseband    | 
     ----------------------------------- 

begann ich in eine meiner App testen Einige alte HTC-Geräte ohne Probleme. Bei Tests mit Samsung-Telefonen konnten später mehrere Geräte keine Verbindung herstellen. Diese Telefone angeblich nicht SPP Profil, nach Hersteller und 3rd Party Spezifikationen (EDIT: 3rd Party Specs Liste SPP als unterstützt, und die Herstellerangaben sind nicht genau genug). Eine IOException (Service Discovery nicht) wurde geworfen, und ich folgte dem Ansatz in dieser Frage gezeigt:

Service discovery failed exception using Bluetooth on Android

Die vorgeschlagene Lösung ist eine Reflexion Hack zu verwenden, wie folgt:

 Method m = device.getClass().getMethod("createRfcommSocket", new Class[] {int.class}); 
     BluetoothSocket socket = socket = (BluetoothSocket) m.invoke(device, 1); 

Der Hack hat für mich funktioniert. Erstaunlicherweise ist diese Methode in der Klasse BluetoothDevice öffentlich, wird jedoch mithilfe der @hide-Annotation aus der API entfernt. Dies ist der Quellcode von JellyBean:

 /** 
     * Create an RFCOMM {@link BluetoothSocket} ready to start a secure 
     * outgoing connection to this remote device on given channel. 
     * <p>The remote device will be authenticated and communication on this 
     * socket will be encrypted. 
     * <p> Use this socket only if an authenticated socket link is possible. 
     * Authentication refers to the authentication of the link key to 
     * prevent man-in-the-middle type of attacks. 
     * For example, for Bluetooth 2.1 devices, if any of the devices does not 
     * have an input and output capability or just has the ability to 
     * display a numeric key, a secure socket connection is not possible. 
     * In such a case, use {#link createInsecureRfcommSocket}. 
     * For more details, refer to the Security Model section 5.2 (vol 3) of 
     * Bluetooth Core Specification version 2.1 + EDR. 
     * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing 
     * connection. 
     * <p>Valid RFCOMM channels are in range 1 to 30. 
     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} 
     * 
     * @param channel RFCOMM channel to connect to 
     * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection 
     * @throws IOException on error, for example Bluetooth not available, or 
     *      insufficient permissions 
     * @hide 
     */ 
     public BluetoothSocket createRfcommSocket(int channel) throws IOException { 
      return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, true, true, this, channel, 
        null); 
     } 

Ich kann nicht verstehen, warum eine öffentliche Methode von der API auf diese Weise entfernt wird. Aber dieses appart lassen, sowohl dieses Verfahren und die offiziell unterstützt eine UUID verwenden, sind dünne Umschläge denselben BluetoothSocket Konstruktor mit unterschiedlichen Parametern aufrufen:

 public BluetoothSocket createRfcommSocketToServiceRecord(UUID uuid) throws IOException { 
      return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, true, true, this, -1, 
        new ParcelUuid(uuid)); 
     } 

ein wenig mehr in den Quellen Graben erkannte ich, dass sowohl eine RFCOMM-Verbindung zu öffnen, aber die UUID-Methode initiiert eine Entdeckung und die versteckte nicht.

Alles in allem funktionieren die Reflection-Hacks einwandfrei in jedem Gerät, das ich getestet habe, von OS 2.2 bis 4.1. BEARBEITEN: Die "fehlerhaften" Geräte unterstützen SPP, es ist nur so, dass ihre benutzerdefinierte Implementierung des BT-Stacks mit dem Erkennungsprozess in Konflikt gerät; Es gibt auch andere Fehler, wie den, der den Kopplungsdialog für bereits gekoppelte Geräte in ICS zeigt. Das Aufrufen dieser versteckten API mithilfe der Reflektion ermöglicht eine Problemumgehung für alle diese Fehler oder das von den verschiedenen Herstellern eingeführte Verhalten.

Sollte ich den Hack im Produktionscode behalten? Gibt es eine Möglichkeit, dasselbe mit der öffentlichen API zu erreichen?

Vielen Dank im Voraus.

Antwort

16

Hervorragende Frage. Grundsätzlich können Sie die Reflexion verwenden, die Sie wollen. Sogar ich habe etwas Ähnliches gemacht, um die Startzeit der Anwendung zu berechnen, habe eine Methode über Reflektion bekommen und sie funktioniert wie ein Zauberspruch von FroYo bis Jelly Bean. Das einzige Wort der Vorsicht Sie trainieren müssen, ist dies,

  • Da es noch keine öffentliche API ist, google es jederzeit
  • ändern
  • ohne Warnung, wenn es die System-Apps oder HAL ändert, die verwendet wird es sein, entsprechend geändert, ohne dass irgendwelche Anwendungen betroffen sind.

Wo müssen Sie vorsichtig sein?

Chancen sind die Argumente dieser Methode möglicherweise in der Zukunft geändert werden.

Sie müssen dies mit jeder neuen OS-Version überprüfen, damit Ihre Anwendung nicht beschädigt wird. Ansonsten müssen Sie sich keine Sorgen über diesen Hack machen. Viele Anwendungen verwenden solche Hacks, wenn Dinge nicht mit der API offengelegt werden.

+8

Nicht nur kann "googlen (sic) es jederzeit ohne Warnung ändern", aber Gerätehersteller können "es jederzeit ohne Warnung ändern". Es gibt keine Garantie dafür, dass diese Methode auf einem bestimmten Android-Gerät existiert oder funktioniert. – CommonsWare

Verwandte Themen