一般的な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;
}
}
}
最終的な完成形は以下の通り:





