hamibot 如何通过蓝牙名称获取蓝牙的MAC?怎么写?
18:23:46.917/E: Wrapped java.lang.SecurityException: Need android.permission.BLUETOOTH_CONNECT permission for android.content.AttributionSource@a41f817e App with package name:com.hamibot.hamibot: AdapterService getBondedDevices (69a914acdf4f97a858c6d4b4.js#22)
Wrapped java.lang.SecurityException: Need android.permission.BLUETOOTH_CONNECT permission for android.content.AttributionSource@a41f817e App with package name:com.hamibot.hamibot: AdapterService getBondedDevices
at 69a914acdf4f97a858c6d4b4.js:22:0 出错了
不修改 Hamibot APK 权限行不行?能否绕开这个权限获取蓝牙MAC
回答
在 Hamibot 脚本中,可以通过调用 Android 原生蓝牙 API 来获取蓝牙设备的 MAC 地址。由于 Android 系统权限限制,你需要确保脚本拥有蓝牙及相关位置权限,并且建议在已配对的设备列表中查找,因为扫描新设备需要异步处理且较为复杂。
以下是实现通过蓝牙名称获取已配对设备 MAC 地址的示例代码:
// 文档地址 https://docs.hamibot.com/
// 请求必要的权限
// 需要蓝牙权限和位置权限(Android 6.0+ 蓝牙扫描需要位置权限)
runtime.requestPermissions(['BLUETOOTH', 'BLUETOOTH_ADMIN', 'ACCESS_FINE_LOCATION']);
// 获取默认的蓝牙适配器
var BluetoothAdapter = android.bluetooth.BluetoothAdapter;
var adapter = BluetoothAdapter.getDefaultAdapter();
// 检查蓝牙是否开启
if (!adapter.isEnabled()) {
log('蓝牙未开启,尝试开启...');
// 注意:部分系统可能不允许脚本直接开启蓝牙,需要用户手动确认
adapter.enable();
}
// 获取已配对的设备列表
var bondedDevices = adapter.getBondedDevices();
var targetName = '目标蓝牙名称'; // 替换为你要查找的蓝牙设备名称
var targetMac = null;
// 遍历已配对设备查找匹配项
if (bondedDevices.size() > 0) {
for (var i = 0; i < bondedDevices.size(); i++) {
var device = bondedDevices[i];
var name = device.getName();
if (name === targetName) {
targetMac = device.getAddress();
log('找到设备:' + name + ', MAC 地址:' + targetMac);
break;
}
}
}
if (targetMac) {
log('获取成功:' + targetMac);
} else {
log('未找到名为 ' + targetName + ' 的已配对设备');
}
注意事项
- 权限要求:脚本运行需要获取
BLUETOOTH、BLUETOOTH_ADMIN以及ACCESS_FINE_LOCATION权限。在 Android 6.0 及以上版本,蓝牙扫描和获取信息通常需要位置权限。 - 已配对设备:上述代码仅能获取已配对(Bonded)的设备。如果需要扫描周围未配对的设备,需要注册
BroadcastReceiver监听蓝牙扫描结果,实现较为复杂且耗时。 - Android 版本限制:Android 10 及以上版本对 MAC 地址访问有严格限制,可能无法获取真实的 MAC 地址,而是返回随机地址或固定值。
- 运行环境:确保脚本在拥有蓝牙硬件的 Android 设备上运行,且 Hamibot 客户端具有相应的系统权限。
如果你需要扫描未配对的设备,建议使用 Android 原生 startDiscovery() 方法配合广播接收器,但这在脚本中实现复杂度较高。对于大多数自动化场景,使用已配对设备列表更为稳定。
补充回答 1
这个错误是因为 Android 12(API 级别 31)及以上版本引入了新的蓝牙权限 BLUETOOTH_CONNECT 和 BLUETOOTH_SCAN。Hamibot 标准版的 Manifest 文件中可能未声明这些新权限,导致脚本调用蓝牙 API 时被系统拦截。
Hamibot 的 runtime.requestPermissions 函数只能申请已在 app Manifest 中声明的权限。标准版 Hamibot 默认只声明了部分常用权限。要解决此问题,你需要修改 Hamibot 应用的 Manifest 文件以增加所需权限。
解决方案
1. 修改 Hamibot APK 权限(必需步骤)
由于 Android 12+ 的限制,你必须通过 APK 编辑器将 BLUETOOTH_CONNECT 权限添加到 Hamibot 应用中:
- 备份并卸载当前手机上的 Hamibot 应用。
- 使用 APK 编辑器(如 MT 管理器、APK Editor 等)打开 Hamibot 的安装包。
- 找到
AndroidManifest.xml文件。 - 在
<manifest>标签内添加以下权限声明:<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> <uses-permission android:name="android.permission.BLUETOOTH_SCAN" /> - 保存并重新签名 APK。
- 安装修改后的 Hamibot 应用。
2. 更新脚本代码
修改 APK 后,脚本中需要动态申请这些新权限。以下是适配 Android 12+ 的代码:
// 文档地址 https://docs.hamibot.com/
// 检查 Android 版本以决定申请哪些权限
var sdkInt = android.os.Build.VERSION.SDK_INT;
var permissions = [];
// 基础蓝牙权限
permissions.push('android.permission.BLUETOOTH');
permissions.push('android.permission.BLUETOOTH_ADMIN');
// Android 6.0+ 需要位置权限用于蓝牙扫描
if (sdkInt >= 23) {
permissions.push('android.permission.ACCESS_FINE_LOCATION');
}
// Android 12+ 需要新的蓝牙连接和扫描权限
if (sdkInt >= 31) {
permissions.push('android.permission.BLUETOOTH_CONNECT');
permissions.push('android.permission.BLUETOOTH_SCAN');
}
// 动态申请权限
runtime.requestPermissions(permissions);
// 获取蓝牙适配器
var BluetoothAdapter = android.bluetooth.BluetoothAdapter;
var adapter = BluetoothAdapter.getDefaultAdapter();
if (adapter == null) {
log('此设备不支持蓝牙');
return;
}
// 检查蓝牙是否开启
if (!adapter.isEnabled()) {
log('蓝牙未开启,请手动开启蓝牙后重试');
// 尝试开启蓝牙,部分系统可能需要用户确认
// adapter.enable();
return;
}
// 获取已配对的设备列表
var bondedDevices = adapter.getBondedDevices();
var targetName = '目标蓝牙名称'; // 替换为你要查找的蓝牙设备名称
var targetMac = null;
// 遍历已配对设备
if (bondedDevices != null && bondedDevices.size() > 0) {
for (var i = 0; i < bondedDevices.size(); i++) {
var device = bondedDevices[i];
// 注意:getName() 和 getAddress() 在 Android 12+ 也需要 BLUETOOTH_CONNECT 权限
var name = device.getName();
if (name != null && name === targetName) {
targetMac = device.getAddress();
log('找到设备:' + name + ', MAC 地址:' + targetMac);
break;
}
}
}
if (targetMac) {
toast('获取成功:' + targetMac);
} else {
toast('未找到名为 ' + targetName + ' 的已配对设备');
}
注意事项
- 权限生效:修改 APK 并重新安装后,首次运行脚本时系统会弹出权限申请对话框,务必点击“允许”。
- MAC 地址限制:即使获取成功,Android 10+ 系统为了隐私保护,可能会返回随机 MAC 地址而非硬件真实 MAC 地址,这是系统级限制,脚本无法绕过。
- 稳定性:修改 APK 可能导致应用签名验证失败,无法覆盖安装或无法使用云脚本功能,建议仅在本地调试使用。
- 已配对设备:代码仅能获取已配对的设备。如果需要扫描未配对设备,逻辑会更复杂且耗电,建议优先使用已配对列表。
如果你无法修改 APK,则在 Android 12 及以上设备上,标准版 Hamibot 可能无法直接通过脚本获取蓝牙 MAC 地址。
补充回答 2
不修改 Hamibot APK 权限,在 Android 12 及以上版本无法通过脚本获取蓝牙 MAC 地址。这是 Android 系统底层的安全限制,无法通过纯脚本代码绕过。
原因分析
- 系统权限拦截:Android 12(API 级别 31)引入了新的蓝牙权限模型。访问蓝牙设备信息(包括 MAC 地址、名称、配对列表)必须持有
BLUETOOTH_CONNECT权限。 - Manifest 声明限制:权限必须在应用的
AndroidManifest.xml中预先声明,才能在运行时申请或使用。Hamibot 标准版安装包未声明该权限,因此即使脚本调用runtime.requestPermissions,系统也不会授予,且调用相关 API 时会直接抛出SecurityException。 - 沙盒机制:脚本运行在 Hamibot 应用的沙盒环境中,权限等级等同于 Hamibot 应用本身。应用没有的权限,脚本无法凭空获得。
替代方案与建议
既然无法直接获取 MAC 地址,建议采用以下替代方案实现你的自动化需求:
1. 使用蓝牙设备名称匹配
大多数自动化场景下,设备名称具有唯一性,足以作为识别标识。
// 文档地址 https://docs.hamibot.com/
var BluetoothAdapter = android.bluetooth.BluetoothAdapter;
var adapter = BluetoothAdapter.getDefaultAdapter();
if (adapter != null && adapter.isEnabled()) {
var bondedDevices = adapter.getBondedDevices();
var targetName = '你的蓝牙耳机'; // 替换为目标设备名称
if (bondedDevices != null && bondedDevices.size() > 0) {
for (var i = 0; i < bondedDevices.size(); i++) {
var device = bondedDevices[i];
var name = device.getName();
if (name != null && name.contains(targetName)) {
log('找到设备:' + name);
// 在此处执行后续操作,无需 MAC 地址
break;
}
}
}
}
2. 确认是否必须使用 MAC 地址
请重新评估业务需求。如果是为了区分设备,设备名称通常足够。如果是为了网络连接,蓝牙连接本身会处理地址映射,脚本通常只需要获取 BluetoothDevice 对象即可进行连接操作,无需显式获取 MAC 字符串。
3. 关于 Root 权限(仅限高级用户)
如果手机已获取 Root 权限,理论上可以通过 shell 命令尝试读取系统底层文件获取蓝牙信息,但这不属于 Hamibot 标准支持范围,且不同手机厂商的系统文件路径不同,稳定性极差。
// 仅作为示例,未 Root 设备无法执行,且路径因厂商而异
// var result = shell("su -c 'ls /sys/class/bluetooth/'", true);
注意:即使有 Root 权限,Android 10+ 系统也对 MAC 地址进行了随机化处理,获取到的可能仍是随机地址而非硬件真实地址。
4. 区分 Wi-Fi MAC 与蓝牙 MAC
Hamibot 提供的 device.getMacAddress() 函数获取的是 WLAN (Wi-Fi) 的 MAC 地址,不是蓝牙 MAC 地址。且该函数需要在有 WLAN 连接的情况下才能返回有效值,无法用于获取蓝牙信息。
总结
在不修改 APK 的前提下,Android 12+ 系统禁止应用获取蓝牙 MAC 地址。建议修改脚本逻辑,使用蓝牙设备名称作为识别依据,这是最稳定且兼容所有 Android 版本的方案。