2015/04/12

Bluetooth Low Energy

Android 4.3 (API Level 18) introduces built-in platform support for Bluetooth Low Energy in the central role and provides APIs that apps can use to discover devices, query for services, and read/write characteristics. In contrast to Classic Bluetooth, Bluetooth Low Energy (BLE) is designed to provide significantly lower power consumption. This allows Android apps to communicate with BLE devices that have low power requirements, such as proximity sensors, heart rate monitors, fitness devices, and so on.

Android4.3(API Level 18)よりBLE向けとなるCentral role APIの提供が開始された.
このAPIを使用することにより, アプリはBLEデバイスを探索・発見することができ, serviceを問い合わせ, characteristicsのread/writeが可能となる.
従来のBluetooth,と比べてBLEは大幅に低い消費電力となるよう設計されている. これはAndroidアプリケーションがBLEデバイスとcommunicateするのに近接センサーや心拍数モニタ, フィットネス機器のように少ない電力で済むことを意味する.

Key Terms and Concepts

Here is a summary of key BLE terms and concepts:

  • Generic Attribute Profile (GATT) —The GATT profile is a general specification for sending and receiving short pieces of data known as “attributes” over a BLE link. All current Low Energy application profiles are based on GATT.

    • The Bluetooth SIG defines many profiles for Low Energy devices. A profile is a specification for how a device works in a particular application. Note that a device can implement more than one profile. For example, a device could contain a heart rate monitor and a battery level detector.
  • Attribute Protocol (ATT) —GATT is built on top of the Attribute Protocol (ATT). This is also referred to as GATT/ATT. ATT is optimized to run on BLE devices. To this end, it uses as few bytes as possible. Each attribute is uniquely identified by a Universally Unique Identifier (UUID), which is a standardized 128-bit format for a string ID used to uniquely identify information. The attributes transported by ATT are formatted as characteristics and services.

  • Characteristic —A characteristic contains a single value and 0-n descriptors that describe the characteristic’s value. A characteristic can be thought of as a type, analogous to a class.

  • Descriptor —Descriptors are defined attributes that describe a characteristic value. For example, a descriptor might specify a human-readable description, an acceptable range for a characteristic’s value, or a unit of measure that is specific to a characteristic’s value.

  • Service —A service is a collection of characteristics. For example, you could have a service called “Heart Rate Monitor” that includes characteristics such as “heart rate measurement.” You can find a list of existing GATT-based profiles and services on bluetooth.org.

これらはBLEのキーワードとコンセプトである.

  • Generic Attribute Profile (GATT) GATTプロファイルはBLE通信によるデータの送信や既知の”attributes”の断片を受信するための基本仕様である. 全てのBLEアプリケーションのプロファイルはGATTプロファイルがベースになる.

    • Bluetooth SIGではいつくかのBLEプロファイルが定義されている. プロファイルはデバイスが特定のアプリケーションでどのように動作するかの仕様である. デバイスは複数のプロファイルを実装できる点には注意すること. 例えばあるデバイスは心拍数モニターと電力消費モニターを含むことができる.
  • Attribute Protocal (ATT) - GATTはAttribute Prorotocol(ATT)の上位に組み込まれる. これはGATT/ATTと呼ばれる. ATTはBLEデバイスでの実行に最適化されており, 少ないバイトの使用で実行できるよう作られている. それぞれのattributeは標準化された128bit形式の文字列IDであるUniversally Unique Identifier(UUID)で識別される. attributesはATTによりcharacteristicsとserviceの形式にされて転送される.

  • Characteristic - characteristicは1つの値と0-nのcharacteristicに言及するdescriptorを含んでいる. characteristicはタイプやクラスに似たものと考えることができる.

  • Descriptor - descriptorはcharacteristicの値を説明するattributeとして定義される. 例えば, characteristicの値が許容可能な範囲を人間が読めるdescriptionとして定義されるものであったり, characteristicの値の単位を表すものであったりする.

  • Service - serviceはcharacteristicのコレクションである. 例えば, “Heart rate Monitor”と呼ばれるサービスには”heart rate measurement”という名のcharacteristicが含まれる. GATT-ベースのプロファルとサービスはbluetooth.orgで見つけることができる.

Roles and Responsibilities

Here are the roles and responsibilities that apply when an Android device interacts with a BLE device:

  • Central vs. peripheral. This applies to the BLE connection itself. The device in the central role scans, looking for advertisement, and the device in the peripheral role makes the advertisement.
  • GATT server vs. GATT client. This determines how two devices talk to each other once they’ve established the connection.

To understand the distinction, imagine that you have an Android phone and an activity tracker that is a BLE device. The phone supports the central role; the activity tracker supports the peripheral role (to establish a BLE connection you need one of each—two things that only support peripheral couldn’t talk to each other, nor could two things that only support central).

Once the phone and the activity tracker have established a connection, they start transferring GATT metadata to one another. Depending on the kind of data they transfer, one or the other might act as the server. For example, if the activity tracker wants to report sensor data to the phone, it might make sense for the activity tracker to act as the server. If the activity tracker wants to receive updates from the phone, then it might make sense for the phone to act as the server.

In the example used in this document, the Android app (running on an Android device) is the GATT client. The app gets data from the GATT server, which is a BLE heart rate monitor that supports the Heart Rate Profile. But you could alternatively design your Android app to play the GATT server role. See BluetoothGattServer for more information.

ここではAndroidデバイスがBLEデバイスと作用し合う際の役割と応答性について述べる.

  • Central vs. peripheral. BLEコネクションに適用される形態である. centralなデバイスはスキャンし, advertisementを探す. peripheralなデバイスはadvertisementを作成する.
  • GATT server vs. GATT client. 2つのデバイス接続確立後の通信方法を決定する.

各役割の違いを理解するためにAndroid PhoneとBLEデバイスであるActivity trackerを例に挙げる.
PhoneはCentralの役割を果たす. Activity trackerはperipheralの役割を果たす(BLE接続するにはPeripheralだけでは成り立たないし, Centralだけでも成り立たない. 両方の役割をもつデバイスが揃う必要がある.)

PhoneとActivity trackerが接続を確立した後, デバイスは互いにGATT metadataの転送を開始する. 転送データの種類に応じてどちらかがserverとして動作する. 例えば, もしActivity trackerがセンサーデータをPhoneにレポートしたい場合, Activity trackerがserverとなるかもしれない. 一方でもしActivity trackerがPhoneからデータを受信してデータをアップデートしたい場合はPhoneがserverになるかもしれない.

このドキュメントではAndroidデバイスで実行されるAndroid アプリケーションはGATT clientとして使用される. アプリはHeart Rate ProfileをサポートするBLE heart rate monitorのデータをGATT serverから受信する. もしGATT serverとして振る舞うアプリを作成したい場合はBluetoothGattServer により詳しい情報が書いてある.

BLE Permissions

In order to use Bluetooth features in your application, you must declare the Bluetooth permission BLUETOOTH. You need this permission to perform any Bluetooth communication, such as requesting a connection, accepting a connection, and transferring data.

If you want your app to initiate device discovery or manipulate Bluetooth settings, you must also declare the BLUETOOTH_ADMIN permission. Note: If you use the BLUETOOTH_ADMIN permission, then you must also have the BLUETOOTH permission.

BLEアプリケーションはBluetooth機能を使用する. そのためにはBLUETOOTHパーミッションを宣言する必要がある. アプリがBluetoothコミュニケーション(接続要求/接続受理/データ転送)をする場合にこのパーミッションを宣言する必要がある.

もしBluetoothに関する設定を操作するのであればBLUETOOTH_ADMINのパーミッションも宣言する必要がある. NOTE: もしBLUETOOTH_ADMINのパーミッションを使用するのであれば同時にBLUETOOTHのパーミッションも必要である.

Declare the Bluetooth permission(s) in your application manifest file. For example:

アプリケーションのAndroidManifestでこれらのパーミッションを宣言するには下記を参照.

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

If you want to declare that your app is available to BLE-capable devices only, include the following in your app’s manifest:

もしBLE機能を備えたデバイスでのみアプリを有効にしたいのであれば次の1行をAndroidManifestに加えると良い. これによりBLE機能を備えていないデバイスではアプリは無効となりインストールされない.

<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>

However, if you want to make your app available to devices that don’t support BLE, you should still include this element in your app’s manifest, but set required="false". Then at run-time you can determine BLE availability by using PackageManager.hasSystemFeature():

しかし, BLE機能を備えていないデバイスでもアプリは有効にしインストールまでは進めたい場合はrequired="false"で宣言しておく. アプリ実行時にBLE機能の有無を調べる場合はPackageManager.hasSystemFeature()を使用する.

// Use this check to determine whether BLE is supported on the device. Then
// you can selectively disable BLE-related features.
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
    Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
    finish();
}

Setting Up BLE

Before your application can communicate over BLE, you need to verify that BLE is supported on the device, and if so, ensure that it is enabled. Note that this check is only necessary if <uses-feature.../> is set to false.

If BLE is not supported, then you should gracefully disable any BLE features. If BLE is supported, but disabled, then you can request that the user enable Bluetooth without leaving your application. This setup is accomplished in two steps, using the BluetoothAdapter.

アプリケーションでBLE通信を始める前に, デバイスがBLE機能を有しているか検証する必要ががる. これはAndroidManifestの<uses-feature.../>のrequired="false"を設定している場合に必要である.

デバイスがBLEをサポートしていないのであればそれに関わる機能を全て無効かする必要がある. BLEをサポートしているものの機能が無効かされている場合, ユーザにこれを有効とするようリクエストすることができる. これにはBluetoothAdapterを使って2つの手順を実行する.

  1. Get the BluetoothAdapter
    The BluetoothAdapter is required for any and all Bluetooth activity. The BluetoothAdapter represents the device’s own Bluetooth adapter (the Bluetooth radio). There’s one Bluetooth adapter for the entire system, and your application can interact with it using this object. The snippet below shows how to get the adapter. Note that this approach uses getSystemService() to return an instance of BluetoothManager, which is then used to get the adapter. Android 4.3 (API Level 18) introduces BluetoothManager:

  2. Enable Bluetooth
    Next, you need to ensure that Bluetooth is enabled. Call isEnabled() to check whether Bluetooth is currently enabled. If this method returns false, then Bluetooth is disabled. The following snippet checks whether Bluetooth is enabled. If it isn’t, the snippet displays an error prompting the user to go to Settings to enable Bluetooth:

  1. BluetoothAdapterを取得する
    BluetoothAdapterはBluetoothを使用する場合に必要となる. BluetoothAdapterはデバイス自身のBluetoothアダプタ(Bluetooth radio)に相当する. システムが持つBluetoothアダプタを使用してアプリケーションは通信することができる. 次のスニペットはadapterを取得するものである. これはgetSystemService()を使用してBluetoothManagerを得るアプローチである. BluetoothManagerはAndroid 4.3(API Level 18)から導入されている.

    // Initializes Bluetooth adapter.
    final BluetoothManager bluetoothManager =
            (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
    mBluetoothAdapter = bluetoothManager.getAdapter();
  2. Bluetoothの有効化
    次にBluetoothを有効化する. isEnabled()でBluetoothが現在有効かどうかを検証できる. もしこのメソッドがfalseを返せばBluetoothは無効化されている. 次のスニペットはBluetoothが有効かどうかを検証し, 無効であればユーザにBluetoothを有効にするよう促すプロンプトを表示する.

    private BluetoothAdapter mBluetoothAdapter;
    ...
    // Ensures Bluetooth is available on the device and it is enabled. If not,
    // displays a dialog requesting user permission to enable Bluetooth.
    if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
        Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
        startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
    }

Finding BLE Device

To find BLE devices, you use the startLeScan() method. This method takes a BluetoothAdapter.LeScanCallback as a parameter. You must implement this callback, because that is how scan results are returned. Because scanning is battery-intensive, you should observe the following guidelines:

  • As soon as you find the desired device, stop scanning.
  • Never scan on a loop, and set a time limit on your scan. A device that was previously available may have moved out of range, and continuing to scan drains the battery.

The following snippet shows how to start and stop a scan:

BLEデバイスを見つけるためにstartLeScan()メソッドが使用できる. このメソッドはBluetoothAdapter.LeScanCallbackをパラメータに取る. あなたはこのcallbackを実装し, デバイススキャン結果を確認する. デバイススキャンはバッテリーを多く消費するため次のガイドラインに従うこと.

  • 期待するデバイスが見つかったらスキャンを停止する.
  • スキャンし続けないこと. timeoutを設けること. 接続されたデバイスが圏外移動し, スキャンを続けているとバッテリーを消費し続けることになる.

次はスキャンの開始と停止のスニペットである.

/**
 * Activity for scanning and displaying available BLE devices.
 */
public class DeviceScanActivity extends ListActivity {

    private BluetoothAdapter mBluetoothAdapter;
    private boolean mScanning;
    private Handler mHandler;

    // Stops scanning after 10 seconds.
    private static final long SCAN_PERIOD = 10000;
    ...
    private void scanLeDevice(final boolean enable) {
        if (enable) {
            // Stops scanning after a pre-defined scan period.
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    mScanning = false;
                    mBluetoothAdapter.stopLeScan(mLeScanCallback);
                }
            }, SCAN_PERIOD);

            mScanning = true;
            mBluetoothAdapter.startLeScan(mLeScanCallback);
        } else {
            mScanning = false;
            mBluetoothAdapter.stopLeScan(mLeScanCallback);
        }
        ...
    }
...
}

If you want to scan for only specific types of peripherals, you can instead call startLeScan(UUID[], BluetoothAdapter.LeScanCallback), providing an array of UUID objects that specify the GATT services your app supports.

Here is an implementation of the BluetoothAdapter.LeScanCallback, which is the interface used to deliver BLE scan results:

もし特定のperipheralのみを探す場合, 代わりにstartLeScan(UUID[], BluetoothAdapter.LeScanCallback)を使うことができる. アプリケーションがサポートしているGATT serviceのUUIDのarrayを提供すればよい.

下記はBluetoothAdapter.LeScanCallbackを実装し, BLEデバイスのスキャン結果を使用するサンプルである.

private LeDeviceListAdapter mLeDeviceListAdapter;
...
// Device scan callback.
private BluetoothAdapter.LeScanCallback mLeScanCallback =
        new BluetoothAdapter.LeScanCallback() {
    @Override
    public void onLeScan(final BluetoothDevice device, int rssi,
            byte[] scanRecord) {
        runOnUiThread(new Runnable() {
           @Override
           public void run() {
               mLeDeviceListAdapter.addDevice(device);
               mLeDeviceListAdapter.notifyDataSetChanged();
           }
       });
   }
};

Note: You can only scan for Bluetooth LE devices or scan for Classic Bluetooth devices, as described in Bluetooth. You cannot scan for both Bluetooth LE and classic devices at the same time.

NOTE: アプリはBLEデバイスがClassic BTデバイスどちらか片方しか一度にスキャンできない. これについてはBluetoothで述べている.

Connecting to a GATT Server

The first step in interacting with a BLE device is connecting to it— more specifically, connecting to the GATT server on the device. To connect to a GATT server on a BLE device, you use the connectGatt() method. This method takes three parameters: a Context object, autoConnect (boolean indicating whether to automatically connect to the BLE device as soon as it becomes available), and a reference to a BluetoothGattCallback:

BLEデバイスと通信するためのfirst stepとしてそれと接続すること. すなわちデバイスからGATT serverに接続する. BLEデバイスがGATT serverに接続するにはconnectGatt()メソッドを使用する. このメソッドはContext, autoConnect(利用可能になったBLEデバイスに自動接続するかどうかのフラグ), そしてBluetoothGattCallbackをパラメータにとる.

mBluetoothGatt = device.connectGatt(this, false, mGattCallback);

This connects to the GATT server hosted by the BLE device, and returns a BluetoothGatt instance, which you can then use to conduct GATT client operations. The caller (the Android app) is the GATT client. The BluetoothGattCallback is used to deliver results to the client, such as connection status, as well as any further GATT client operations.

In this example, the BLE app provides an activity (DeviceControlActivity) to connect, display data, and display GATT services and characteristics supported by the device. Based on user input, this activity communicates with a Service called BluetoothLeService, which interacts with the BLE device via the Android BLE API:

これはBLEデバイスによってホストされたGATT serverへの接続と, BluetoothGattのインスタンスを返却する. BluetoothGattインスタンスでGATT clientとしての操作が可能になる. 呼び出し元であるAndroid アプリがGATT clientとして振る舞う時, BluetoothGattCallbackは接続状態や他のGATT client操作を伝送するために使用される.

今回の例では, BLEアプリはDeviceControlActivityでBLEデバイスに接続し, データを表示し, GATT serviceとデバイスがサポートしているcharacteristicsを表示する. ユーザ入力を契機に
Android BLE APIを使用してBLEデバイスと通信するBluetoothLeServiceとコミュニケーションする.

// A service that interacts with the BLE device via the Android BLE API.
public class BluetoothLeService extends Service {
    private final static String TAG = BluetoothLeService.class.getSimpleName();

    private BluetoothManager mBluetoothManager;
    private BluetoothAdapter mBluetoothAdapter;
    private String mBluetoothDeviceAddress;
    private BluetoothGatt mBluetoothGatt;
    private int mConnectionState = STATE_DISCONNECTED;

    private static final int STATE_DISCONNECTED = 0;
    private static final int STATE_CONNECTING = 1;
    private static final int STATE_CONNECTED = 2;

    public final static String ACTION_GATT_CONNECTED =
            "com.example.bluetooth.le.ACTION_GATT_CONNECTED";
    public final static String ACTION_GATT_DISCONNECTED =
            "com.example.bluetooth.le.ACTION_GATT_DISCONNECTED";
    public final static String ACTION_GATT_SERVICES_DISCOVERED =
            "com.example.bluetooth.le.ACTION_GATT_SERVICES_DISCOVERED";
    public final static String ACTION_DATA_AVAILABLE =
            "com.example.bluetooth.le.ACTION_DATA_AVAILABLE";
    public final static String EXTRA_DATA =
            "com.example.bluetooth.le.EXTRA_DATA";

    public final static UUID UUID_HEART_RATE_MEASUREMENT =
            UUID.fromString(SampleGattAttributes.HEART_RATE_MEASUREMENT);

    // Various callback methods defined by the BLE API.
    private final BluetoothGattCallback mGattCallback =
            new BluetoothGattCallback() {
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status,
                int newState) {
            String intentAction;
            if (newState == BluetoothProfile.STATE_CONNECTED) {
                intentAction = ACTION_GATT_CONNECTED;
                mConnectionState = STATE_CONNECTED;
                broadcastUpdate(intentAction);
                Log.i(TAG, "Connected to GATT server.");
                Log.i(TAG, "Attempting to start service discovery:" +
                        mBluetoothGatt.discoverServices());

            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                intentAction = ACTION_GATT_DISCONNECTED;
                mConnectionState = STATE_DISCONNECTED;
                Log.i(TAG, "Disconnected from GATT server.");
                broadcastUpdate(intentAction);
            }
        }

        @Override
        // New services discovered
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
            } else {
                Log.w(TAG, "onServicesDiscovered received: " + status);
            }
        }

        @Override
        // Result of a characteristic read operation
        public void onCharacteristicRead(BluetoothGatt gatt,
                BluetoothGattCharacteristic characteristic,
                int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
            }
        }
     ...
    };
...
}

When a particular callback is triggered, it calls the appropriate broadcastUpdate() helper method and passes it an action. Note that the data parsing in this section is performed in accordance with the Bluetooth Heart Rate Measurement profile specifications:

特定のcallbackをトリガした際に, 適切なbroadcastUpdate()ヘルパメソッドを呼び出しactionを渡す. このセクションでBluetooth Heart Rate Measurementプロファイルの使用に従ってデータをパースする.

private void broadcastUpdate(final String action) {
    final Intent intent = new Intent(action);
    sendBroadcast(intent);
}

private void broadcastUpdate(final String action,
                             final BluetoothGattCharacteristic characteristic) {
    final Intent intent = new Intent(action);

    // This is special handling for the Heart Rate Measurement profile. Data
    // parsing is carried out as per profile specifications.
    if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {
        int flag = characteristic.getProperties();
        int format = -1;
        if ((flag & 0x01) != 0) {
            format = BluetoothGattCharacteristic.FORMAT_UINT16;
            Log.d(TAG, "Heart rate format UINT16.");
        } else {
            format = BluetoothGattCharacteristic.FORMAT_UINT8;
            Log.d(TAG, "Heart rate format UINT8.");
        }
        final int heartRate = characteristic.getIntValue(format, 1);
        Log.d(TAG, String.format("Received heart rate: %d", heartRate));
        intent.putExtra(EXTRA_DATA, String.valueOf(heartRate));
    } else {
        // For all other profiles, writes the data formatted in HEX.
        final byte[] data = characteristic.getValue();
        if (data != null && data.length > 0) {
            final StringBuilder stringBuilder = new StringBuilder(data.length);
            for(byte byteChar : data)
                stringBuilder.append(String.format("%02X ", byteChar));
            intent.putExtra(EXTRA_DATA, new String(data) + "\n" +
                    stringBuilder.toString());
        }
    }
    sendBroadcast(intent);
}

Back in DeviceControlActivity, these events are handled by a BroadcastReceiver:

DeviceControlActivityに戻って, これらのイベントはBroadcastReceiverでハンドルされる.

// Handles various events fired by the Service.
// ACTION_GATT_CONNECTED: connected to a GATT server.
// ACTION_GATT_DISCONNECTED: disconnected from a GATT server.
// ACTION_GATT_SERVICES_DISCOVERED: discovered GATT services.
// ACTION_DATA_AVAILABLE: received data from the device. This can be a
// result of read or notification operations.
private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        final String action = intent.getAction();
        if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) {
            mConnected = true;
            updateConnectionState(R.string.connected);
            invalidateOptionsMenu();
        } else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) {
            mConnected = false;
            updateConnectionState(R.string.disconnected);
            invalidateOptionsMenu();
            clearUI();
        } else if (BluetoothLeService.
                ACTION_GATT_SERVICES_DISCOVERED.equals(action)) {
            // Show all the supported services and characteristics on the
            // user interface.
            displayGattServices(mBluetoothLeService.getSupportedGattServices());
        } else if (BluetoothLeService.ACTION_DATA_AVAILABLE.equals(action)) {
            displayData(intent.getStringExtra(BluetoothLeService.EXTRA_DATA));
        }
    }
};

Reading BLE Attributes

Once your Android app has connected to a GATT server and discovered services, it can read and write attributes, where supported. For example, this snippet iterates through the server’s services and characteristics and displays them in the UI:

AndroidアプリがGATT serverへの接続を保持し, serviceを発見し, それのattributeを読み書きでき, それらがサポートされている場合. 例えば次のスニペットはGATT serverがもつserviceとcharacteristicsを反復しながらUI表示するものである.

public class DeviceControlActivity extends Activity {
    ...
    // Demonstrates how to iterate through the supported GATT
    // Services/Characteristics.
    // In this sample, we populate the data structure that is bound to the
    // ExpandableListView on the UI.
    private void displayGattServices(List<BluetoothGattService> gattServices) {
        if (gattServices == null) return;
        String uuid = null;
        String unknownServiceString = getResources().
                getString(R.string.unknown_service);
        String unknownCharaString = getResources().
                getString(R.string.unknown_characteristic);
        ArrayList<HashMap<String, String>> gattServiceData =
                new ArrayList<HashMap<String, String>>();
        ArrayList<ArrayList<HashMap<String, String>>> gattCharacteristicData
                = new ArrayList<ArrayList<HashMap<String, String>>>();
        mGattCharacteristics =
                new ArrayList<ArrayList<BluetoothGattCharacteristic>>();

        // Loops through available GATT Services.
        for (BluetoothGattService gattService : gattServices) {
            HashMap<String, String> currentServiceData =
                    new HashMap<String, String>();
            uuid = gattService.getUuid().toString();
            currentServiceData.put(
                    LIST_NAME, SampleGattAttributes.
                            lookup(uuid, unknownServiceString));
            currentServiceData.put(LIST_UUID, uuid);
            gattServiceData.add(currentServiceData);

            ArrayList<HashMap<String, String>> gattCharacteristicGroupData =
                    new ArrayList<HashMap<String, String>>();
            List<BluetoothGattCharacteristic> gattCharacteristics =
                    gattService.getCharacteristics();
            ArrayList<BluetoothGattCharacteristic> charas =
                    new ArrayList<BluetoothGattCharacteristic>();
           // Loops through available Characteristics.
            for (BluetoothGattCharacteristic gattCharacteristic :
                    gattCharacteristics) {
                charas.add(gattCharacteristic);
                HashMap<String, String> currentCharaData =
                        new HashMap<String, String>();
                uuid = gattCharacteristic.getUuid().toString();
                currentCharaData.put(
                        LIST_NAME, SampleGattAttributes.lookup(uuid,
                                unknownCharaString));
                currentCharaData.put(LIST_UUID, uuid);
                gattCharacteristicGroupData.add(currentCharaData);
            }
            mGattCharacteristics.add(charas);
            gattCharacteristicData.add(gattCharacteristicGroupData);
         }
    ...
    }
...
}

Receiving GATT Notifications

It’s common for BLE apps to ask to be notified when a particular characteristic changes on the device. This snippet shows how to set a notification for a characteristic, using the setCharacteristicNotification() method:

BLEアプリに共通することとして, デバイス上で特定のcharacteristicの変更通知を受け取ることがある. 次のスニペットはsetCharacteristicNotification()メソッドを用いてcharacteristicの変更通知を受け取る方法である.

private BluetoothGatt mBluetoothGatt;
BluetoothGattCharacteristic characteristic;
boolean enabled;
...
mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
...
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
        UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptor);

Once notifications are enabled for a characteristic, an onCharacteristicChanged() callback is triggered if the characteristic changes on the remote device:

characteristicの変更通知が有効になると, リモートデバイスのcharacteristicが変更されるとonCharacteristicChanged()が呼ばれる.

@Override
// Characteristic notification
public void onCharacteristicChanged(BluetoothGatt gatt,
        BluetoothGattCharacteristic characteristic) {
    broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}

Closing the Client App

Once your app has finished using a BLE device, it should call close() so the system can release resources appropriately:

アプリがBLEデバイスの使用を終える時, close()メソッドを呼び出し, システムが適切にリソースを解放できるようにすること.

public void close() {
    if (mBluetoothGatt == null) {
        return;
    }
    mBluetoothGatt.close();
    mBluetoothGatt = null;
}

参考

License:
Portions of this page are modifications based on work created and shared by the Android Open Source Project and used according to terms described in the Creative Commons 2.5 Attribution License.

2015/03/30

Android : Deprecated HTTP Classes on Android5.1

Deprecated HTTP Classes on Android5.1

The org.apache.http classes and the AndroidHttpClient class have been deprecated in Android 5.1. These classes are no longer being maintained and you should migrate any app code using these APIs to the URLConnection classes as soon as possible.

org.apache.httpパッケージとAndroidHttpClientクラスはAndroid5.1で非推奨扱いとなる. 今後これらのクラスはメンテナンスされることがない. これらのAPIを使用しているアプリケーションはURLConnectionクラスへの変更が推奨される.

Deprecated APIs

org.apache.http.* と AndroidHttpClient

org.apache.http.impl.client.DefaultHttpClientとそのサブクラスであるAndroidHttpClientはWebブラウザ向けに最適化されたHTTPクライアントである. これらのAPIは巨大でフレキシブルなAPIを備えており安定した実装であるが, いくつかのバグを含んでいる.

巨大なAPIであるため互換性を維持しながら改善することが困難であり, AndroidチームはこれらのAPIを積極的に保守することをしていない.

今回, Android5.1のリリースを迎えてこれらのAPIは公式に保守されなく(Deprecatedと)なった.

HttpURLConnection

HttpURLConnection多くのアプリケーションに適応できる汎用かつ軽量なHTTPクライアントである. 軽量であるため保守性にも優れている. Android5.1で非推奨となったHttpClient系のかわりにこちらを使用すること.

HttpURLConnectionはAndroid Froyo以前で重大なバグを抱えている(Issue 2939).
ReadableなInputStreamからclose()を呼ぶとConnection poolを汚染することがある. Connection pooling を無効にしてこの問題を回避する方法は下記.

private void disableConnectionReuseIfNecessary() {
    // HTTP connection reuse which was buggy pre-froyo
    if (Integer.parseInt(Build.VERSION.SDK) < Build.VERSION_CODES.FROYO) {
        System.setProperty("http.keepAlive", "false");
    }
}

このバグはGingerbreadで改修されている.
さらにGingerbreadからtransparent response compressionに対応し, ICSからはResponse cacheに対応している.

以降はURLConnection系クラスのoverviewである.

URLConnection Class Overview

A connection to a URL for reading or writing. For HTTP connections, see HttpURLConnection for documentation of HTTP-specific features.
For example, to retrieve ftp://mirror.csclub.uwaterloo.ca/index.html:

読み書きのためURLへ接続する. HTTPコネクションはHTTP機能に特化したHttpURLConnectionを参照.
ftp://mirror.csclub.uwaterloo.ca/index.htmlを取得するサンプルは下記.

   URL url = new URL("ftp://mirror.csclub.uwaterloo.ca/index.html");
   URLConnection urlConnection = url.openConnection();
   InputStream in = new BufferedInputStream(urlConnection.getInputStream());
   try {
     readStream(in);
    finally {
     in.close();
   }

URLConnection must be configured before it has connected to the remote resource. Instances of URLConnection are not reusable: you must use a different instance for each connection to a resource.

URLConnectionの設定はリモートリソースに接続する前に行うこと. URLConnectionのインスタンスは再利用できない. 異なるリソースへの接続は異なるインスタンスで行う事.

Timeouts

URLConnection supports two timeouts: a connect timeout and a read timeout. By default, operations never time out.

URLConnectionは接続タイムアウトとリードタイムアウトの2種類をサポートする. デフォルトでは操作でタイムアウトすることは無い.

Built-in Protocols

  • File
    Resources from the local file system can be loaded using file: URIs. File connections can only be used for input.

  • FTP
    File Transfer Protocol (RFC 959) is supported, but with no public subclass. FTP connections can be used for input or output but not both.
    By default, FTP connections will be made using anonymous as the username and the empty string as the password. Specify alternate usernames and passwords in the URL: ftp://username:password@host/path.

  • HTTP and HTTPS
    Refer to the HttpURLConnection and HttpsURLConnection subclasses.

  • Jar
    Refer to the JarURLConnection subclass.

  • File
    ローカルファイルシステムのリソースに対してはfile:スキームを使用する. File接続はinputのみ使用できる.

  • FTP
    File Transfer Protocol(RFC959)がサポートされているが, 公開されたサブクラスはない. FTPではinputかoutputの片方を使用することができ, 両方の使用はできない.

  • HTTP and HTTPS
    HttpURLConnectionとHttpsURLConnectionのサブクラスを参照.

  • Jar
    JarURLConnectionのサブクラスを参照.

Registering Additional Protocols

Use setURLStreamHandlerFactory(URLStreamHandlerFactory) to register handlers for other protocol types.

setURLStreamHandlerFactory(URLStreamHandlerFactory)を使い, 異なるプロトコル種別のハンドラを設定する.

HttpURLConnection Class Overview

An URLConnection for HTTP (RFC 2616) used to send and receive data over the web. Data may be of any type and length. This class may be used to send and receive streaming data whose length is not known in advance. Uses of this class follow a pattern:

URLConnectionはHTTP(RFC2616)を使用したWebを介したデータ送受信のために使用される. データの種類や長さは任意. このクラスは事前に長さが知らされていないストリーミングデータを受信するのにも使用できる.
このクラスの使用パターンは下記.

  1. Obtain a new HttpURLConnection by calling URL.openConnection() and casting the result to HttpURLConnection.
  2. Prepare the request. The primary property of a request is its URI. Request headers may also include metadata such as credentials, preferred content types, and session cookies.
  3. Optionally upload a request body. Instances must be configured with setDoOutput(true) if they include a request body. Transmit data by writing to the stream returned by getOutputStream().
  4. Read the response. Response headers typically include metadata such as the response body’s content type and length, modified dates and session cookies. The response body may be read from the stream returned by getInputStream(). If the response has no body, that method returns an empty stream.
  5. Disconnect. Once the response body has been read, the HttpURLConnection should be closed by calling disconnect(). Disconnecting releases the resources held by a connection so they may be closed or reused.
  1. 新しいHttpURLConnectionを取得する. URL.openConnection()を呼び, HttpURLConnectionにキャストする.

  2. リクエストを準備する. 主なプロパティはURIである. リクエストヘッダには認証情報, コンテンツ種別, セッションクッキーのメタデータが含まれる.

  3. 必要に応じてリクエストボディをアップロードする. リクエストボディを含めるにはsetDoOutput(true)を設定し, getOutputStream()で返却されるストリームにデータを書き込む.

  4. レスポンスを読み込む. レスポンスヘッダには一般的にレスポンスボディのコンテントタイプ, 長さ, 更新日とセッションクッキーといったメタデータが含まれる. レスポンスボディはgetInputStream()で取得できるストリームから読み込める. レスポンスにボディが含まれない場合, このメソッドは空のストリームを返す.

  5. コネクションの切断. レスポンスボディが読み込まれた後, HttpURLConnectionをdisconnect()メソッドで閉じる必要がある. コネクションを切断する事で関連するリソースを開放し, コネクションが再利用できるようになる.

For example, to retrieve the webpage at http://www.android.com/:

   URL url = new URL("http://www.android.com/");
   HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
   try {
     InputStream in = new BufferedInputStream(urlConnection.getInputStream());
     readStream(in);
    finally {
     urlConnection.disconnect();
   }

Secure Communication with HTTPS

Calling openConnection() on a URL with the “https” scheme will return an HttpsURLConnection, which allows for overriding the default HostnameVerifier and SSLSocketFactory. An application-supplied SSLSocketFactory created from an SSLContext can provide a custom X509TrustManager for verifying certificate chains and a custom X509KeyManager for supplying client certificates. See HttpsURLConnection for more details.

“https”スキーム付きのURLでopenConnection()を呼び出すとHttpsURLConnectionが返却される. これはデフォルトで使用されるHostnameVerifierとSSLSocketFactoryのオーバライドを可能にする.
SSLContextから生成されるアプリケーション提供のSSLSocketFactoryは, 証明書チェーンのためのカスタムX509TrustManagerと, クライアント証明書のためのカスタムX509KeyManagerを提供する.

Response Handling

HttpURLConnection will follow up to five HTTP redirects. It will follow redirects from one origin server to another. This implementation doesn’t follow redirects from HTTPS to HTTP or vice versa.
If the HTTP response indicates that an error occurred, getInputStream() will throw an IOException. Use getErrorStream() to read the error response. The headers can be read in the normal way using getHeaderFields(),

HttpURLConnectionは5回分のHTTPリダイレクトをフォローアップする. オリジンサーバから異なるサーバへのリダイレクトがフォローされる. この実装はHTTPsからHTTPへのリダイレクトやその逆はフォローされない. もしHTTPレスポンスがエラーを示す場合, getInputStream()がIOExceptionをスローする. getErrorStream()を使用してエラーレスポンスを読み込むことができる. 通常通りgetHeaderFields()でヘッダを読み込むことができる.

Posting Content

To upload data to a web server, configure the connection for output using setDoOutput(true).
For best performance, you should call either setFixedLengthStreamingMode(int) when the body length is known in advance, or setChunkedStreamingMode(int) when it is not. Otherwise HttpURLConnection will be forced to buffer the complete request body in memory before it is transmitted, wasting (and possibly exhausting) heap and increasing latency.

WebサーバにデータをアップロードするにはsetDoOutput(true)でコネクションを出力用に設定する. パフォーマンスのために, ボディの長さが判明している場合はsetFixedLengthStreamingMode(int)を設定する. そうでない場合ははsetChunkedStreamingMode(int)を設定する. そうでない場合HttpURLConnectionはリクエスト送信を終えるまでリクエストボディを全てメモリにバッファするためヒープと待ち時間をいたずらに消費する.

For example, to perform an upload:

   HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
   try {
     urlConnection.setDoOutput(true);
     urlConnection.setChunkedStreamingMode(0);

     OutputStream out = new BufferedOutputStream(urlConnection.getOutputStream());
     writeStream(out);

     InputStream in = new BufferedInputStream(urlConnection.getInputStream());
     readStream(in);
    finally {
     urlConnection.disconnect();
   }
 }

Performance

The input and output streams returned by this class are not buffered. Most callers should wrap the returned streams with BufferedInputStream or BufferedOutputStream. Callers that do only bulk reads or writes may omit buffering.
When transferring large amounts of data to or from a server, use streams to limit how much data is in memory at once. Unless you need the entire body to be in memory at once, process it as a stream (rather than storing the complete body as a single byte array or string).

URLConnectionクラスはバッファされないinput / output streamを返す. 多くの場合これらをBufferedInputStream / BufferedOutputStreamでラップする. もし一度にまとめて読み書きしたい場合にはバッファリングは省略されるだろう.
サーバへ/から巨大なデータを転送する場合, 確保必要なメモリ容量を抑えるためにstreamは使用される. もしボディーの情報を全てメモリ上に展開してバイト配列や文字列として扱う
必要でも無い限りstreamを使用すること.

To reduce latency, this class may reuse the same underlying Socket for multiple request/response pairs. As a result, HTTP connections may be held open longer than necessary. Calls to disconnect() may return the socket to a pool of connected sockets. This behavior can be disabled by setting the http.keepAlive system property to false before issuing any HTTP requests. The http.maxConnections property may be used to control how many idle connections to each server will be held.

待ち時間を短縮するために, このクラスは対となる複数の要求/応答に同じSocketを使用する.
しかし, この結果HTTPコネクションは必要以上に保持される.
disconnect()を呼ぶと接続済SocketプールにSocketが返却される. この振る舞いはHTTPリクエストの開始より前にシステムプロパティhttp.keepAliveをfalseに設定することで無効化できる. http.maxConnectionsプロパティは各サーバ毎にどれだけのidleコネクションを保持させるかを制御する.

By default, this implementation of HttpURLConnection requests that servers use gzip compression. Since getContentLength() returns the number of bytes transmitted, you cannot use that method to predict how many bytes can be read from getInputStream(). Instead, read that stream until it is exhausted: when read() returns -1. Gzip compression can be disabled by setting the acceptable encodings in the request header:

HttpURLConnectionの標準実装では, サーバがgzip圧縮を使用するリクエストを投げる. getContentLength()は送信されたバイト数を返却するため, getInputStream()でどれだけのバイトが読み込まれるかを予測するためにこの数字を使用することはできない. かわりに, streamのread()が-1を返すまで読み続ける. Gzip圧縮はリクエストヘッダのAccept-Encodingで無効化することができる.

urlConnection.setRequestProperty("Accept-Encoding", "identity");

Handling Network Sign-On

Some Wi-Fi networks block Internet access until the user clicks through a sign-on page. Such sign-on pages are typically presented by using HTTP redirects. You can use getURL() to test if your connection has been unexpectedly redirected. This check is not valid until after the response headers have been received, which you can trigger by calling getHeaderFields() or getInputStream(). For example, to check that a response was not redirected to an unexpected host:

いくつかのWi-Fiネットワークはユーザのサインオンページを経由してインターネットにアクセスする. このようなサインオンページは通常HTTPリダイレクトを使用して提供される.
getURL()を使用することで期待しないリダイレクトによる接続であるかをテストできる. このチェックはレスポンスヘッダの受信後では有効ではない. getHeaderFields()かgetInputStream()を呼び出す際にトリガーすることができる. 次のコードは期待しないホストへリダイレクトされていないことをチェックするものである.

   HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
   try {
     InputStream in = new BufferedInputStream(urlConnection.getInputStream());
     if (!url.getHost().equals(urlConnection.getURL().getHost())) {
       // we were redirected! Kick the user out to the browser to sign on?

     ...
   } finally {
     urlConnection.disconnect();
   }

HTTP Authentication

HttpURLConnection supports HTTP basic authentication. Use Authenticator to set the VM-wide authentication handler:

HttpURLConnectionはHTTPベーシック認証をサポートする. VM全体で使う認証ハンドラにAuthenticator
を設定するには次,

   Authenticator.setDefault(new Authenticator() {
     protected PasswordAuthentication getPasswordAuthentication() {
       return new PasswordAuthentication(username, password.toCharArray());

   });
 }

Unless paired with HTTPS, this is not a secure mechanism for user authentication. In particular, the username, password, request and response are all transmitted over the network without encryption.

これはHTTPSで使用されない限りセキュアでないユーザ認証である. ユーザ名, パスワードや要求/応答は暗号化されずにネットワークを介してデータ送信される.

Sessions with Cookies

To establish and maintain a potentially long-lived session between client and server, HttpURLConnection includes an extensible cookie manager. Enable VM-wide cookie management using CookieHandler and CookieManager:

クライアントとサーバ間の潜在的なセッションを長時間確立するため, HttpURLConnectionは拡張可能なcookie managerを包含している. CookieHandlerとCookieManagerを使ってVM全体のCookie管理を有効にする.

   CookieManager cookieManager = new CookieManager();
   CookieHandler.setDefault(cookieManager);

By default, CookieManager accepts cookies from the origin server only. Two other policies are included: ACCEPT_ALL and ACCEPT_NONE. Implement CookiePolicy to define a custom policy.
The default CookieManager keeps all accepted cookies in memory. It will forget these cookies when the VM exits. Implement CookieStore to define a custom cookie store.

標準で, CookieManagerはorigin serverからのみcookieを受け入れる. 異なる2つのポリシーとして, ACCEPT_ALLとACCEPT_NONEが用意されている. カスタムポリシーを定義するためにはCookiePolicyを実装する. デフォルトのCookieManagerは記憶された受け入れcookieを全て保持する. VMが終了するとこれらのcookieは忘れられる. カスタムcookieを記録するにはCookieStoreを実装する.

In addition to the cookies set by HTTP responses, you may set cookies programmatically. To be included in HTTP request headers, cookies must have the domain and path properties set.
By default, new instances of HttpCookie work only with servers that support RFC 2965 cookies. Many web servers support only the older specification, RFC 2109. For compatibility with the most web servers, set the cookie version to 0.

HTTPレスポンスで設定されるcookieの他に, プログラムでcookieを設定することができる. HTTPリクエストヘッダに含まれるようにcookieが設定されているドメインとパスのプロパティを設定する.

For example, to receive www.twitter.com in French:

フランス語でwww.twitter.comを受信する例は以下の通り。

   HttpCookie cookie = new HttpCookie("lang", "fr");
   cookie.setDomain("twitter.com");
   cookie.setPath("/");
   cookie.setVersion(0);
   cookieManager.getCookieStore().add(new URI("http://twitter.com/"), cookie);

HTTP Methods

HttpURLConnection uses the GET method by default. It will use POST if setDoOutput(true) has been called. Other HTTP methods (OPTIONS, HEAD, PUT, DELETE and TRACE) can be used with setRequestMethod(String).

HttpURLConnectionは標準でGETメソッドを使用する. POSTメソッドを使うにはsetDoOutput(true)を使用する. 他のHTTPメソッド(OPTIONS, HEAD, PUT, DELETE, TRACE)はsetRequestMethod(String)を使用する.

Proxies

By default, this class will connect directly to the origin server. It can also connect via an HTTP or SOCKS proxy. To use a proxy, use URL.openConnection(Proxy) when creating the connection.

標準でこのクラスはorigin serverへ直接接続する. これはHTTPかSOCKS proxy経由で接続することができる. Proxyを使用するにはコネクションを生成するのにURL.openConnection(Proxy)を使用する.

IPv6 Support

This class includes transparent support for IPv6. For hosts with both IPv4 and IPv6 addresses, it will attempt to connect to each of a host’s addresses until a connection is established.

このクラスはIPv6をサポートする. ホストがIPv4とIPv6両方をサポートする場合, 接続が確立されるまで両方での接続を試みます.

Response Caching

Android 4.0 (Ice Cream Sandwich, API level 15) includes a response cache. See android.net.http.HttpResponseCache for instructions on enabling HTTP caching in your application.

Android4.0でresponse cacheが追加された. アプリケーションでHTTPキャッシングを有効にする方法についてはandroid.net.http.HttpResponseCacheを参照.

Avoiding Bugs In Earlier Releases

Prior to Android 2.2 (Froyo), this class had some frustrating bugs. In particular, calling close() on a readable InputStream could poison the connection pool. Work around this by disabling connection pooling:

Android2.2以前, このクラスにはイライラするバグを抱えている. 具体的にはreadableなInputStreamでclose()を呼んだ時コネクションプールを汚染するというものである. コネクションプーリングを無効化することでこの問題を回避できる.

  private void disableConnectionReuseIfNecessary() {
     // Work around pre-Froyo bugs in HTTP connection reuse.
    if (Integer.parseInt(Build.VERSION.SDK) < Build.VERSION_CODES.FROYO) {
      System.setProperty("http.keepAlive", "false");
    }
  }

Each instance of HttpURLConnection may be used for one request/response pair. Instances of this class are not thread safe.

HttpURLConnectionのインスタンスは要求/応答のペアに対して1つ使用できる. このクラスはスレッドセーフではない.

HttpsURLConnection Class Overview

An HttpURLConnection for HTTPS (RFC 2818). A connected HttpsURLConnection allows access to the negotiated cipher suite, the server certificate chain, and the client certificate chain if any.

HTTPS(RFC2818)のためのHttpURLConnection. HttpsURLConnectionによる接続はChiperスイートによるネゴシエーション, サーバ証明書チェーン, クライアント証明書チェーンがある場合にアクセスを可能とする.

Providing an application specific X509TrustManager

If an application wants to trust Certificate Authority (CA) certificates that are not part of the system, it should specify its own X509TrustManager via a SSLSocketFactory set on the HttpsURLConnection. The X509TrustManager can be created based on a KeyStore using a TrustManagerFactory to supply trusted CA certificates. Note that self-signed certificates are effectively their own CA and can be trusted by including them in a KeyStore.
For example, to trust a set of certificates specified by a KeyStore:

アプリケーションがシステムの一部ではないCA証明書を信頼したい場合, HttpsURLConnectionでSSLSocketFactoryを介して独自のX509TrustManagerを設定する.

   KeyStore keyStore = ...;
   String algorithm = TrustManagerFactory.getDefaultAlgorithm();
   TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
   tmf.init(keyStore);

   SSLContext context = SSLContext.getInstance("TLS");
   context.init(null, tmf.getTrustManagers(), null);

   URL url = new URL("https://www.example.com/");
   HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
   urlConnection.setSSLSocketFactory(context.getSocketFactory());
   InputStream in = urlConnection.getInputStream();

It is possible to implement X509TrustManager directly instead of using one created by a TrustManagerFactory. While this is straightforward in the insecure case of allowing all certificate chains to pass verification, writing a proper implementation will usually want to take advantage of CertPathValidator. In general, it might be better to write a custom KeyStore implementation to pass to the TrustManagerFactory than to try and write a custom X509TrustManager.

TrustManagerFactoryによって生成されたものではなく, 直接X509TrustManagerを実装することができる. 全ての証明書チェーンが検証をパスしないと安全でないような簡単なケースで実装にはCertPathValidatorを使用したいと考えるだろう. 一般的にカスタムされたX509TrustManagerを実装するよりはTrustManagerFactoryにカスタムKeyStoreを渡すほうが良い方法となる.

Providing an application specific X509KeyManager

A custom X509KeyManager can be used to supply a client certificate and its associated private key to authenticate a connection to the server. The X509KeyManager can be created based on a KeyStore using a KeyManagerFactory.
For example, to supply client certificates from a KeyStore:

カスタムされたX509KeyManagerはクライアント証明書と秘密鍵で関連づけられたサーバへの接続の認証に使用できる. X509KeyManagerはKeyManagerFactoryを使用してKeyStoreを基に作成できる.

   KeyStore keyStore = ...;
   String algorithm = KeyManagerFactory.getDefaultAlgorithm();
   KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
   kmf.init(keyStore);

   SSLContext context = SSLContext.getInstance("TLS");
   context.init(kmf.getKeyManagers(), null, null);

   URL url = new URL("https://www.example.com/");
   HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
   urlConnection.setSSLSocketFactory(context.getSocketFactory());
   InputStream in = urlConnection.getInputStream();

A X509KeyManager can also be implemented directly. This can allow an application to return a certificate and private key from a non-KeyStore source or to specify its own logic for selecting a specific credential to use when many may be present in a single KeyStore.

X509KeyManagerは直接実装できる. これはアプリケーションにKeyStoreをソースとしない秘密鍵と証明書を返却させるか, 多くは1つのKeyStoreに存在する特定の証明書を選択する独自ロジックを実装する.

TLS Intolerance Support

This class attempts to create secure connections using common TLS extensions and SSL deflate compression. Should that fail, the connection will be retried with SSLv3 only.

このクラスは一般的なTLS拡張とSSL deflate圧縮を使用したセキュアなコネクションを生成する. 接続が失敗した際にはSSLv3での再接続を試みる.

APIs

参考

License:
Portions of this page are modifications based on work created and shared by the Android Open Source Project and used according to terms described in the Creative Commons 2.5 Attribution License.