上次讲解Android的蓝牙基本用法,这次讲得深入些,探讨下蓝牙方面的隐藏API。用过Android系统设置(Setting)的人都知道蓝牙搜索之后可以建立配对和解除配对,但是这两项功能的函数没有在SDK中给出,那么如何去使用这两项功能呢?本文利用JAVA的反射机制去调用这两项功能对应的函数:createBond和removeBond,具体的发掘和实现步骤如下:

1、使用Git工具下载platform/packages/apps/Settings.git,在Setting源码中查找关于建立配对和解除配对的API,知道这两个API的宿主(BluetoothDevice);

2、使用反射机制对BluetoothDevice枚举其所有方法和常量,看看是否存在:

static public void printAllInform(Class clsShow) {
try {
// 取得所有方法
Method[] hideMethod = clsShow.getMethods();
int i = 0;
for (; i < hideMethod.length; i++) {
Log.e("method name", hideMethod[i].getName());
}
// 取得所有常量
Field[] allFields = clsShow.getFields();
for (i = 0; i < allFields.length; i++) {
Log.e("Field name", allFields[i].getName());
}
} catch (SecurityException e) {
// throw new RuntimeException(e.getMessage());
e.printStackTrace();
} catch (IllegalArgumentException e) {
// throw new RuntimeException(e.getMessage());
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

结果如下:

11-29 09:19:12.012: method name(452): cancelBondProcess
11-29 09:19:12.020: method name(452): cancelPairingUserInput
11-29 09:19:12.020: method name(452): createBond
11-29 09:19:12.020: method name(452): createInsecureRfcommSocket
11-29 09:19:12.027: method name(452): createRfcommSocket
11-29 09:19:12.027: method name(452): createRfcommSocketToServiceRecord
11-29 09:19:12.027: method name(452): createScoSocket
11-29 09:19:12.027: method name(452): describeContents
11-29 09:19:12.035: method name(452): equals
11-29 09:19:12.035: method name(452): fetchUuidsWithSdp
11-29 09:19:12.035: method name(452): getAddress
11-29 09:19:12.035: method name(452): getBluetoothClass
11-29 09:19:12.043: method name(452): getBondState
11-29 09:19:12.043: method name(452): getName
11-29 09:19:12.043: method name(452): getServiceChannel
11-29 09:19:12.043: method name(452): getTrustState
11-29 09:19:12.043: method name(452): getUuids
11-29 09:19:12.043: method name(452): hashCode
11-29 09:19:12.043: method name(452): isBluetoothDock
11-29 09:19:12.043: method name(452): removeBond
11-29 09:19:12.043: method name(452): setPairingConfirmation
11-29 09:19:12.043: method name(452): setPasskey
11-29 09:19:12.043: method name(452): setPin
11-29 09:19:12.043: method name(452): setTrust
11-29 09:19:12.043: method name(452): toString
11-29 09:19:12.043: method name(452): writeToParcel
11-29 09:19:12.043: method name(452): convertPinToBytes
11-29 09:19:12.043: method name(452): getClass
11-29 09:19:12.043: method name(452): notify
11-29 09:19:12.043: method name(452): notifyAll
11-29 09:19:12.043: method name(452): wait
11-29 09:19:12.051: method name(452): wait
11-29 09:19:12.051: method name(452): wait

3、如果枚举发现API存在(SDK却隐藏),则自己实现调用方法:

/**
* 与设备配对 参考源码:platform/packages/apps/Settings.git
* /Settings/src/com/android/settings/bluetooth/CachedBluetoothDevice.java
*/
static public boolean createBond(Class btClass,BluetoothDevice btDevice) throws Exception {
Method createBondMethod = btClass.getMethod("createBond");
Boolean returnValue = (Boolean) createBondMethod.invoke(btDevice);
return returnValue.booleanValue();
}
/**
* 与设备解除配对 参考源码:platform/packages/apps/Settings.git
* /Settings/src/com/android/settings/bluetooth/CachedBluetoothDevice.java
*/
static public boolean removeBond(Class btClass,BluetoothDevice btDevice) throws Exception {
Method removeBondMethod = btClass.getMethod("removeBond");
Boolean returnValue = (Boolean) removeBondMethod.invoke(btDevice);
return returnValue.booleanValue();
}

PS : SDK之所以不给出隐藏的API肯定有其原因,也许是出于安全性或者是后续版本兼容性的考虑,因此不能保证隐藏API能在所有Android平台上很好地运行。。。

本文程序运行效果如下:

main.xml源码如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout
android:id="@+id/LinearLayout01"
android:layout_height="wrap_content"
android:layout_width="fill_parent">
<Button
android:id="@+id/btnSearch"
android:layout_width="160dip"
android:layout_height="wrap_content"
android:text="Search">
</Button>
<Button
android:id="@+id/btnShow"
android:layout_height="wrap_content"
android:layout_width="160dip"
android:text="Show"
</Button>
</LinearLayout>
<LinearLayout
android:id="@+id/LinearLayout02"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</LinearLayout>
<ListView
android:id="@+id/ListView01"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
</ListView>
</LinearLayout>

工具类ClsUtils.java源码如下:

package com.testReflect;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import android.bluetooth.BluetoothDevice;
import android.util.Log;
public class ClsUtils {
/**
* 与设备配对 参考源码:platform/packages/apps/Settings.git
* /Settings/src/com/android/settings/bluetooth/CachedBluetoothDevice.java
*/
static public boolean createBond(Class btClass, BluetoothDevice btDevice)
throws Exception {
Method createBondMethod = btClass.getMethod("createBond");
Boolean returnValue = (Boolean) createBondMethod.invoke(btDevice);
return returnValue.booleanValue();
}
/**
* 与设备解除配对 参考源码:platform/packages/apps/Settings.git
* /Settings/src/com/android/settings/bluetooth/CachedBluetoothDevice.java
*/
static public boolean removeBond(Class btClass, BluetoothDevice btDevice)
throws Exception {
Method removeBondMethod = btClass.getMethod("removeBond");
Boolean returnValue = (Boolean) removeBondMethod.invoke(btDevice);
return returnValue.booleanValue();
}
/**
* @param clsShow
*/
static public void printAllInform(Class clsShow) {
try {
// 取得所有方法
Method[] hideMethod = clsShow.getMethods();
int i = 0;
for (; i < hideMethod.length; i++) {
Log.e("method name", hideMethod[i].getName());
}
// 取得所有常量
Field[] allFields = clsShow.getFields();
for (i = 0; i < allFields.length; i++) {
Log.e("Field name", allFields[i].getName());
}
} catch (SecurityException e) {
// throw new RuntimeException(e.getMessage());
e.printStackTrace();
} catch (IllegalArgumentException e) {
// throw new RuntimeException(e.getMessage());
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

主程序testReflect.java的源码如下:

package com.testReflect;
import java.util.ArrayList;
import java.util.List;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.Toast;
public class testReflect extends Activity {
Button btnSearch, btnShow;
ListView lvBTDevices;
ArrayAdapter<String> adtDevices;
List<String> lstDevices = new ArrayList<String>();
BluetoothDevice btDevice;
BluetoothAdapter btAdapt;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btnSearch = (Button) this.findViewById(R.id.btnSearch);
btnSearch.setOnClickListener(new ClickEvent());
btnShow = (Button) this.findViewById(R.id.btnShow);
btnShow.setOnClickListener(new ClickEvent());
lvBTDevices = (ListView) this.findViewById(R.id.ListView01);
adtDevices = new ArrayAdapter<String>(testReflect.this,
android.R.layout.simple_list_item_1, lstDevices);
lvBTDevices.setAdapter(adtDevices);
lvBTDevices.setOnItemClickListener(new ItemClickEvent());
btAdapt = BluetoothAdapter.getDefaultAdapter();// 初始化本机蓝牙功能
if (btAdapt.getState() == BluetoothAdapter.STATE_OFF)// 开蓝牙
btAdapt.enable();
// 注册Receiver来获取蓝牙设备相关的结果
IntentFilter intent = new IntentFilter();
intent.addAction(BluetoothDevice.ACTION_FOUND);
intent.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
registerReceiver(searchDevices, intent);
}
private BroadcastReceiver searchDevices = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Bundle b = intent.getExtras();
Object[] lstName = b.keySet().toArray();
// 显示所有收到的消息及其细节
for (int i = 0; i < lstName.length; i++) {
String keyName = lstName[i].toString();
Log.e(keyName, String.valueOf(b.get(keyName)));
}
// 搜索设备时,取得设备的MAC地址
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
BluetoothDevice device = intent
.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (device.getBondState() == BluetoothDevice.BOND_NONE) {
String str = "未配对|" + device.getName() + "|"
+ device.getAddress();
lstDevices.add(str); // 获取设备名称和mac地址
adtDevices.notifyDataSetChanged();
}
}
}
};
class ItemClickEvent implements AdapterView.OnItemClickListener {
@Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
long arg3) {
btAdapt.cancelDiscovery();
String str = lstDevices.get(arg2);
String[] values = str.split("//|");
String address = values[2];
btDevice = btAdapt.getRemoteDevice(address);
try {
if (values[0].equals("未配对")) {
Toast.makeText(testReflect.this, "由未配对转为已配对", 500).show();
ClsUtils.createBond(btDevice.getClass(), btDevice);
} else if (values[0].equals("已配对")) {
Toast.makeText(testReflect.this, "由已配对转为未配对", 500).show();
ClsUtils.removeBond(btDevice.getClass(), btDevice);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/**
* 按键处理
* @author GV
*/
class ClickEvent implements View.OnClickListener {
@Override
public void onClick(View v) {
if (v == btnSearch) {// 搜索附近的蓝牙设备
lstDevices.clear();
Object[] lstDevice = btAdapt.getBondedDevices().toArray();
for (int i = 0; i < lstDevice.length; i++) {
BluetoothDevice device = (BluetoothDevice) lstDevice[i];
String str = "已配对|" + device.getName() + "|"
+ device.getAddress();
lstDevices.add(str); // 获取设备名称和mac地址
adtDevices.notifyDataSetChanged();
}
// 开始搜索
setTitle("本机蓝牙地址:" + btAdapt.getAddress());
btAdapt.startDiscovery();
} else if (v == btnShow) {// 显示BluetoothDevice的所有方法和常量,包括隐藏api
ClsUtils.printAllInform(btDevice.getClass());
}
}
}
}

安卓开发之探秘蓝牙隐藏API相关推荐

  1. 手把手教你使用更多的原生安卓开发app的框架frida API

    点击上方"Python爬虫与数据挖掘",进行关注 回复"书籍"即可获赠Python从入门到进阶共10本电子书 今 日 鸡 汤 蜡烛有心还惜别,替人垂泪到天明. ...

  2. 安卓开发设置全屏隐藏标题栏

    修改AndroidManifest.xml文件中的android:theme即可(点一个NoActionBar). android:theme="@style/Theme.AppCompat ...

  3. 安卓基于BLE的蓝牙开发入门

    BLE蓝牙开发简单入门 BLE背景介绍 引言 BLE简介 Gatt协议以及必备知识 蓝牙开发涉及的API介绍 BLE实战准备 真机调试 权限准备 写两个简单的页面 扫描设备主界面 扫描设备信息界面 实 ...

  4. 安卓开发之使用第三方的聚合数据API,QQ测吉凶案、身份证号码查询。

    在安卓开发中,肯定需要很多API接口, 比如天气获.快递实时信息.身份证号码查询和基本的短信验证码. API(Application Programming Interface,应用程序编程接口)是一 ...

  5. 安卓开发: Jetpack compose + kotlin 实现 俄罗斯方块游戏

    文章目录 前言 俄罗斯方块开发文档 1.摘要 2.开发工具选取 2.1.Compose 的自身优点 2.2.数据驱动界面 3.设计需求 3.1.功能需求 3.1.1.基本游戏功能 3.1.2.拓展功能 ...

  6. 安卓通讯之《蓝牙单片机通讯助手》②扫描设备、连接设备和双向通讯。

    前言 上篇文章我们介绍到了开发经典蓝牙和单片机通讯的过程,安卓通讯之<蓝牙单片机通讯助手>①集成工作 ,我们这里还要兼容最新的安卓6.0及以上的系统,因为从6.0以后的权限机制和以往的不一 ...

  7. 安卓开发开发规范手册V1.0

    安卓开发开发规范手册V1.0 之前发布过一份Web安全开发规范手册V1.0,看到收藏文章的读者挺多,发现整理这些文档还挺有意义. 最近周末抽了些时间把之前收集关于安卓安全开发的资料也整理了一下,整理出 ...

  8. 14天学会安卓开发(第一天)Android架构与环境搭建

    14天学会安卓开发 作者:神秘的N (英文名  corder_raine) 联系方式:369428455(反馈) 交流群 :284552167(示例,原文档下载) 版权为作者所有,如有转载请注明出处 ...

  9. 幻樱の安卓开发学习笔记(持续更新)

    安卓开发手册Java . 前言 . 本篇博客是我在开发过程中遇到的一些问题,我将这些问题记录了下来,以防踩重复的坑,希望对需要学习或者来看我踩坑的人有所帮助. . . . . 零.一些常用的依赖 1. ...

最新文章

  1. 使用WebRTC搭建前端视频聊天室——数据通道篇
  2. 基于zookeeper的高可用集群
  3. C++为什么空格无法输出_C 语言 第8章-字符输入/输出和输入验证
  4. 前端学习(2122):项目演示
  5. linux 定时执行kettle6,linux下使用crond定时执行kettle的job
  6. 对汉诺塔递归算法的理解(图解,附完整代码实现)
  7. linux运行好麻烦,解析用Linux非常困难、必须用命令行、很老又丑陋及无法运行游戏...
  8. iphone同步助手_iPhone 与安卓手机之间如何进行资料迁移
  9. JavaScript联网开发架构
  10. Ionic如何实现单选二级菜单切换
  11. 英美安全机构称俄罗斯正在暴力攻击全球数百家组织机构
  12. win10wifi间歇性断网重启后恢复_实用!Xbox Series X 在重启后也能快速恢复之前的游戏状态...
  13. PHP学习笔记:利用gd库给图片打图片水印
  14. linux卸载jdk权限不够,linux中卸载jdk,一个简单有关问题整了一上午
  15. 致初入职场的兄弟姐妹
  16. DB2 SQLCODE常见错误代码
  17. 技嘉显卡 RGBFusion 不能调光解决方法
  18. javascript之键盘事件
  19. [conda报错 已解决]An unexpected error has occurred. Conda has prepared the above report.
  20. RocketMQ Message相关命令【实战笔记】

热门文章

  1. ●BZOJ 4408 [Fjoi 2016]神秘数
  2. javascript简单介绍
  3. [Android] 输入系统(三):加载按键映射
  4. 编写自己的Javascript库-1
  5. C++项目库包含,dll引用问题,直接把缺失的dll或库放置可执行文件里
  6. C#类的属性遍历及属性值获取
  7. 如何在C#控件中画点并获得指定点的像素颜色
  8. IOS关于键盘的弹出和收起
  9. IOS开发基础之微博项目第1天-OC版
  10. 如何改善mysql数据装载操作效率的方法_详述如何提高MySQL中数据装载效率