Android提高之蓝牙隐藏API探秘
前面文章讲解了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(); }
此处注意: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:layout_height="wrap_content" android:id="@+id/btnsearch" android:text="search" android:layout_width="160dip"></button> <button android:layout_height="wrap_content" android:layout_width="160dip" android:text="show" android:id="@+id/btnshow"></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()); } } } }
希望本文实例能够对大家进行android程序开发有一定的借鉴帮助作用。