22

Bluetooth経由でプリンターに接続する必要があるプロジェクトで働いています。プリンターの製造元は、SPP(シリアルポートプロファイル)を備えたAndroidフォンのみがプリンターに接続できると述べています。

これが私が最初に接続を開いた方法です:

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

JellyBeanの時点で、UUIDを使用することが、AndroidパブリックAPIを使用してRFCOMM接続を開く唯一の方法です。UUIDが不要なBlackBerryとJavaMEでSPP接続を使用したことがあるので、これは少し奇妙だと思いました。UUIDはサービス検出に関するものです。つまり、SDPを使用してデバイスに存在するサービスについてクエリを実行します。プリンターは事前にペアリングされており、SPPをサポートしていることがわかっているので、実際に検出を開始する必要はありません。しかし、それはまさにそのBluetoothDevice.createRfcommSocketToServiceRecord方法と安全でないバージョンが行うことです。これはSPPスタックであり、SDPが同じ層でどのように異なるプロトコルであるかを確認できるため、最初に検出を開始せずにRFCOMMを使用できるはずです。

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

いくつかの古いHTCデバイスで問題なくアプリのテストを開始しました。その後、Samsungの電話でテストしたところ、いくつかのデバイスが接続を開くことができませんでした。伝えられるところによると、これらの電話は、メーカーとサードパーティの両方の仕様によると、SPPプロファイルをサポートしていません編集:サードパーティの仕様にはSPPがサポートされていると記載されており、メーカーの仕様は十分に正確ではありません)。IOException(サービス検出に失敗しました)がスローされ、この質問に示されているアプローチに従いました。

AndroidでBluetoothを使用したサービス検出に失敗した例外

そこで提案された解決策は、次のようにリフレクションハックを使用することです。

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

ハックは私のために働いた。驚くべきことに、BluetoothDeviceクラス内のこのメソッドはパブリックですが、@hideアノテーションによってAPIから削除されています。これは、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);
        }

この方法でパブリックメソッドがAPIから削除される理由がわかりません。ただし、このappartを使用すると、このメソッドとUUIDを使用して公式にサポートされているメソッドの両方がBluetoothSocket、異なるパラメーターを使用して同じコンストラクターを呼び出す薄いエンベロープになります。

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

ソースをもう少し掘り下げると、どちらもRFCOMM接続を開くことがわかりましたが、UUIDメソッドは検出を開始し、非表示のメソッドは開始しません。

全体として、リフレクションハックは、OS 2.2から4.1まで、私がテストしたすべてのデバイスで問題なく機能します。編集:「障害のある」デバイスはSPPをサポートしていますが、BTスタックのカスタム実装が検出プロセスを台無しにしているだけです。ICSですでにペアリングされているデバイスのペアリングダイアログを表示するバグのような他のバグもあります。リフレクションを使用してこの非表示のAPIを呼び出すと、これらすべてのバグや、さまざまなメーカーによって導入されたさまざまな動作に対する回避策が可能になります

本番コードのハックを維持する必要がありますか?パブリックAPIで同じことを実現する方法はありますか?

前もって感謝します。

4

1 に答える 1

16

素晴らしい質問です。基本的に、あなたはあなたが望むすべての反射を使うことができます。私でさえ、アプリケーションの起動時間を計算するために同様のことを行い、リフレクションを介してメソッドを取得し、FroYoからJellyBeanまでチャームのように機能しました。あなたが行使する必要がある唯一の注意の言葉はこれです、

  • パブリックAPIではないため、Googleは警告なしにいつでも変更できます
  • 変更された場合、それを使用するシステムアプリまたはHALは、アプリケーションに影響を与えることなく、それに応じて変更されます。

どこに注意する必要がありますか?

このメソッドの引数は、将来変更される可能性があります。

したがって、アプリケーションが壊れないように、新しいOSリリースごとにこれを確認する必要があります。それ以外の場合は、このハックの使用について心配する必要はありません。多くのアプリケーションは、APIで公開されていない場合に、このようなハックを使用します。

于 2012-10-10T13:34:23.833 に答える