前面文章讲解了android的蓝牙基本用法,本文讲得深入些,探讨下蓝牙方面的隐藏api。用过android系统设置(setting)的人都知道蓝牙搜索之后可以建立配对和解除配对,但是这两项功能的函数没有在sdk中给出,那么如何去使用这两项功能呢?本文利用java的反射机制去调用这两项功能对应的函数:createbond和removebond,具体的发掘和实现步骤如下:
1.使用git工具下载platform/packages/apps/settings.git,在setting源码中查找关于建立配对和解除配对的api,知道这两个api的宿主(bluetoothdevice);
2.使用反射机制对bluetoothdevice枚举其所有方法和常量,看看是否存在:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
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却隐藏),则自己实现调用方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
/**
* 与设备配对 参考源码: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源码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
<?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源码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
|
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的源码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
|
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程序开发有一定的借鉴帮助作用。