blog

AndroidはBluetoothデバイスをスキャンし、デバイスの種類を取得する

現在の一般的なBluetooth 4.0規格は、従来のBluetoothと低消費電力のBluetoothモジュールが含まれています。 上記のデバイスオブジェクトを取得する メソッドの戻り値があり、上記...

Jun 17, 2020 · 6 min. read
シェア

一般的なBluetooth 4.0規格には、レガシーBluetoothと低消費電力Bluetoothモジュールの両方が含まれています。

ここでは、従来のBluetoothモジュールをスキャンし、その結果を最初に示します:

まず、スキャン

AndroidのBluetoothデバイスのスキャンは、BluetoothAdapterを通じてBluetooth検索を開始し、スキャン結果をブロードキャストで受信することで行われます:

1、BluetoothAdapterを取得

シングルトン・パターンで直接取得可能

val btAdapt = BluetoothAdapter.getDefaultAdapter()

2、スキャン開始

if (!btAdapt.isDiscovering) {
 it.startDiscovery()
}

3.放送登録

val intent = IntentFilter()
intent.apply {
 addAction(BluetoothDevice.ACTION_FOUND)
 addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED)
 addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)
 addAction(BluetoothAdapter.ACTION_STATE_CHANGED)
 priority = IntentFilter.SYSTEM_HIGH_PRIORITY
}
registerReceiver(searchDevices, intent)
private val searchDevices: BroadcastReceiver = object : BroadcastReceiver() {
 override fun onReceive(context: Context, intent: Intent) {
 when (intent.action) {
 BluetoothAdapter.ACTION_STATE_CHANGED -> {
 LogUtil.LOGE("ACTION_STATE_CHANGED")
 }
 BluetoothDevice.ACTION_FOUND -> { //found device
 val device =
 intent.getParcelableExtra<BluetoothDevice>(BluetoothDevice.EXTRA_DEVICE)
 if (!device.name.isNullOrEmpty()) {
 // デバイスオブジェクトを取得する
 mData.add(device)
 adapter.notifyDataSetChanged()
 }
 }
 BluetoothAdapter.ACTION_DISCOVERY_STARTED -> {
 ToastUtil.show("スキャン")
 }
 BluetoothAdapter.ACTION_DISCOVERY_FINISHED -> {
 ToastUtil.show("スキャンが完了し、リストのデバイスをクリックして接続しようとする")。
 }
 }
 }
}

デバイスオブジェクトを取得したら、それをリストに表示することができます!

次に、Bluetoothデバイスタイプを取得します。

上記で取得したデバイスオブジェクトBluetoothDeviceにはdevice.getType()メソッドがあります。ソースコードを見ると、このメソッドが返す型は

/**
 * Bluetooth device type, Unknown 不明なタイプ
 */
public static final int DEVICE_TYPE_UNKNOWN = 0;
/**
 * Bluetooth device type, Classic - BR/EDR devices 伝統的なタイプ
 */
public static final int DEVICE_TYPE_CLASSIC = 1;
/**
 * Bluetooth device type, Low Energy - LE-only ble 
 */
public static final int DEVICE_TYPE_LE = 2;
/**
 * Bluetooth device type, Dual Mode - BR/EDR/LE 伝統的なとbleダブルタイプ
 */
public static final int DEVICE_TYPE_DUAL = 3;

携帯電話付属のBluetoothでスキャンした結果を見てみましょう:

device.getType()メソッドの戻り値を上記のものと比較すると、このメソッドはデバイスが携帯電話タイプなのか、コンピュータタイプなのか、ヘッドセットタイプなのかを区別できないことがわかります。

では、アンドロイドはどのように差別化を図っているのでしょうか?

コマンドラインに以下のコマンドを入力します:

adb shell dumpsys window | findstr mCurrentFocus

携帯電話システム付属のBluetoothスキャンインターフェースの結果を以下のように確認してください:

mCurrentFocus=Window{43e2635 u0 com.android.settings/com.android.settings.Settings$BluetoothSettingsActivity}

BluetoothSettingsActivityそこで、アンドロイドのソースコードをチェックして、それがどのように達成されるかを見ることができることを見つけるために、ここでソースコードは、より深い調査を行うことはありません、次のように方法の種類を取得します:

// タイプに対応するアイコンリソースを取得する
int deviceTypeImg = BtUtil.getDeviceType(device.getBluetoothClass());
holder.ivType.setImageResource(deviceTypeImg);

使用したツールクラスは、次のシステムのソースコードに修正を加えたもので、コードは次のとおりです:

public class BtUtil {
 public static final int PROFILE_HEADSET = 0;
 public static final int PROFILE_A2DP = 1;
 public static final int PROFILE_OPP = 2;
 public static final int PROFILE_HID = 3;
 public static final int PROFILE_PANU = 4;
 public static final int PROFILE_NAP = 5;
 public static final int PROFILE_A2DP_SINK = 6;
 public static int getDeviceType(BluetoothClass bluetoothClass) {
 if (bluetoothClass == null) {
 return R.drawable.ic_settings_bluetooth;
 }
 switch (bluetoothClass.getMajorDeviceClass()) {
 case BluetoothClass.Device.Major.COMPUTER:
 return R.drawable.ic_bt_laptop;
 case BluetoothClass.Device.Major.PHONE:
 return R.drawable.ic_bt_cellphone;
 case BluetoothClass.Device.Major.PERIPHERAL:
 return R.drawable.ic_bt_misc_hid;
 case BluetoothClass.Device.Major.IMAGING:
 return R.drawable.ic_bt_imaging;
 default:
 if (BtUtil.doesClassMatch(bluetoothClass, PROFILE_HEADSET))
 return R.drawable.ic_bt_headset_hfp;
 else if (BtUtil.doesClassMatch(bluetoothClass, PROFILE_A2DP)) {
 return R.drawable.ic_bt_headphones_a2dp;
 } else {
 return R.drawable.ic_settings_bluetooth;
 }
 }
 }
 public static boolean doesClassMatch(BluetoothClass bluetoothClass, int profile) {
 if (profile == PROFILE_A2DP) {
 if (bluetoothClass.hasService(BluetoothClass.Service.RENDER)) {
 return true;
 }
 // By the A2DP spec, sinks must indicate the RENDER service.
 // However we found some that do not (Chordette). So lets also
 // match on some other class bits.
 switch (bluetoothClass.getDeviceClass()) {
 case BluetoothClass.Device.AUDIO_VIDEO_HIFI_AUDIO:
 case BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES:
 case BluetoothClass.Device.AUDIO_VIDEO_LOUDSPEAKER:
 case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
 return true;
 default:
 return false;
 }
 } else if (profile == PROFILE_A2DP_SINK) {
 if (bluetoothClass.hasService(BluetoothClass.Service.CAPTURE)) {
 return true;
 }
 // By the A2DP spec, srcs must indicate the CAPTURE service.
 // However if some device that do not, we try to
 // match on some other class bits.
 switch (bluetoothClass.getDeviceClass()) {
 case BluetoothClass.Device.AUDIO_VIDEO_HIFI_AUDIO:
 case BluetoothClass.Device.AUDIO_VIDEO_SET_TOP_BOX:
 case BluetoothClass.Device.AUDIO_VIDEO_VCR:
 return true;
 default:
 return false;
 }
 } else if (profile == PROFILE_HEADSET) {
 // The render service class is required by the spec for HFP, so is a
 // pretty good signal
 if (bluetoothClass.hasService(BluetoothClass.Service.RENDER)) {
 return true;
 }
 // Just in case they forgot the render service class
 switch (bluetoothClass.getDeviceClass()) {
 case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
 case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
 case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
 return true;
 default:
 return false;
 }
 } else if (profile == PROFILE_OPP) {
 if (bluetoothClass.hasService(BluetoothClass.Service.OBJECT_TRANSFER)) {
 return true;
 }
 switch (bluetoothClass.getDeviceClass()) {
 case BluetoothClass.Device.COMPUTER_UNCATEGORIZED:
 case BluetoothClass.Device.COMPUTER_DESKTOP:
 case BluetoothClass.Device.COMPUTER_SERVER:
 case BluetoothClass.Device.COMPUTER_LAPTOP:
 case BluetoothClass.Device.COMPUTER_HANDHELD_PC_PDA:
 case BluetoothClass.Device.COMPUTER_PALM_SIZE_PC_PDA:
 case BluetoothClass.Device.COMPUTER_WEARABLE:
 case BluetoothClass.Device.PHONE_UNCATEGORIZED:
 case BluetoothClass.Device.PHONE_CELLULAR:
 case BluetoothClass.Device.PHONE_CORDLESS:
 case BluetoothClass.Device.PHONE_SMART:
 case BluetoothClass.Device.PHONE_MODEM_OR_GATEWAY:
 case BluetoothClass.Device.PHONE_ISDN:
 return true;
 default:
 return false;
 }
 } else if (profile == PROFILE_HID) {
 return (bluetoothClass.getDeviceClass() & BluetoothClass.Device.Major.PERIPHERAL) == BluetoothClass.Device.Major.PERIPHERAL;
 } else if (profile == PROFILE_PANU || profile == PROFILE_NAP) {
 // No good way to distinguish between the two, based on class bits.
 if (bluetoothClass.hasService(BluetoothClass.Service.NETWORKING)) {
 return true;
 }
 return (bluetoothClass.getDeviceClass() & BluetoothClass.Device.Major.NETWORKING) == BluetoothClass.Device.Major.NETWORKING;
 } else {
 return false;
 }
 }
}

最終的な完成形は以下の通り:

Read next

ルーターが内モンゴル銀行の柔軟で安全な3Gサービスプラットフォームを構築する

3Gアクセス技術の高帯域幅と移動性の特性を利用して、内モンゴル中國銀行の業務部門の顧客へのサービス対応とサポート能力が大幅に向上しました。

Jun 17, 2020 · 1 min read