上次讲解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 try { // Method[] int i 0 ; for (; Log.e( "method , } // Field[] for (i 0 ; Log.e( "Field , } } catch (SecurityException // e.printStackTrace(); } catch (IllegalArgumentException // e.printStackTrace(); } catch (Exception // 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
19
|
/** * * */ static public boolean createBond(Class throws Exception Method "createBond" ); Boolean return returnValue.booleanValue(); } /** * * */ static public boolean removeBond(Class throws Exception Method "removeBond" ); Boolean return returnValue.booleanValue(); } |
PS : SDK之所以不给出隐藏的API肯定有其原因,也许是出于安全性或者是后续版本兼容性的考虑,因此不能保证隐藏API能在所有Android平台上很好地运行。。。
本文程序运行效果如下:
main.xml源码如下:
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
|
<? xml version = "1.0" encoding = "utf-8" ?> 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源码如下:
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
|
package com.testReflect; import java.lang.reflect.Field; import java.lang.reflect.Method; import android.bluetooth.BluetoothDevice; import android.util.Log; public class ClsUtils /** * * */ static public boolean createBond(Class throws Exception Method "createBond" ); Boolean return returnValue.booleanValue(); } /** * * */ static public boolean removeBond(Class throws Exception Method "removeBond" ); Boolean return returnValue.booleanValue(); } /** * * */ static public void printAllInform(Class try { // Method[] int i 0 ; for (; Log.e( "method , } // Field[] for (i 0 ; Log.e( "Field , } } catch (SecurityException // e.printStackTrace(); } catch (IllegalArgumentException // e.printStackTrace(); } catch (Exception // 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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
|
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 ListView ArrayAdapter<String> List<String> new ArrayList<String>(); BluetoothDevice BluetoothAdapter @Override public void onCreate(Bundle super .onCreate(savedInstanceState); setContentView(R.layout.main); btnSearch this .findViewById(R.id.btnSearch); btnSearch.setOnClickListener( new ClickEvent()); btnShow this .findViewById(R.id.btnShow); btnShow.setOnClickListener( new ClickEvent()); lvBTDevices this .findViewById(R.id.ListView01); adtDevices new ArrayAdapter<String>(testReflect. this , android.R.layout.simple_list_item_1, lvBTDevices.setAdapter(adtDevices); lvBTDevices.setOnItemClickListener( new ItemClickEvent()); btAdapt // if (btAdapt.getState() // btAdapt.enable(); // IntentFilter new IntentFilter(); intent.addAction(BluetoothDevice.ACTION_FOUND); intent.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); registerReceiver(searchDevices, } private BroadcastReceiver new BroadcastReceiver() public void onReceive(Context String Bundle Object[] // for ( int i 0 ; String Log.e(keyName, } // if (BluetoothDevice.ACTION_FOUND.equals(action)) BluetoothDevice .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); if (device.getBondState() String "未配对|" + "|" + lstDevices.add(str); // adtDevices.notifyDataSetChanged(); } } } }; class ItemClickEvent implements AdapterView.OnItemClickListener @Override public void onItemClick(AdapterView<?> int arg2, long arg3) btAdapt.cancelDiscovery(); String String[] "//|" ); String 2 ]; btDevice try { if (values[ 0 ].equals( "未配对" )) Toast.makeText(testReflect. this , "由未配对转为已配对" , 500 ).show(); ClsUtils.createBond(btDevice.getClass(), } else if (values[ 0 ].equals( "已配对" )) Toast.makeText(testReflect. this , "由已配对转为未配对" , 500 ).show(); ClsUtils.removeBond(btDevice.getClass(), } } catch (Exception // e.printStackTrace(); } } } /** * * * * */ class ClickEvent implements View.OnClickListener @Override public void onClick(View if (v // lstDevices.clear(); Object[] for ( int i 0 ; BluetoothDevice String "已配对|" + "|" + lstDevices.add(str); // adtDevices.notifyDataSetChanged(); } // setTitle( "本机蓝牙地址:" + btAdapt.startDiscovery(); } else if (v // ClsUtils.printAllInform(btDevice.getClass()); } } } } |