藍芽通話功能原始碼解析
3 藍芽耳機服務
在開啟藍芽過程中,會開啟一些對應的服務,在此只將和通話相關的一個服務, HeadsetClientService。手機上只有開啟了這個服務,才可以將該手機當作一個藍芽耳機,通話時聲音才可以傳輸過來。
3.1 準備
首先在啟動apk時,會首先啟動該apk的Application,然後才是其它元件,因此, Application可以進行一些初始化的操作。Bluetooth.apk 對應的Application是
AdapterApp(classAdapterApp extends Application),其中完成2見事情:
1,載入對應的jni庫。
1. static {
2. if (DBG) Log.d(TAG,"Loading JNILibrary");
3. System.loadLibrary("bluetooth_jni");
4. }
2,呼叫Config 檢查哪些服務可以啟動。
5. @Override
6. public void onCreate() {
7. super.onCreate();
8. if (DBG) Log.d(TAG,"onCreate");
9. Config.init(this);
10. }
Config裡面僅有3個static 方法,只做2見事情。
11. public class Config {
12. private static final String TAG ="AdapterServiceConfig";
13. //List of profile services.
14. @SuppressWarnings("rawtypes")
15. //Do not inclue OPP and PBAP, because theirservices
16. //are not managed by AdapterService
17. private static final Class[] PROFILE_SERVICES = {
18. HeadsetService.class,
19. A2dpService.class,
20. A2dpSinkService.class,
21. HidService.class,
22. HealthService.class,
23. PanService.class,
24. GattService.class,
25. BluetoothMapService.class,
26. HeadsetClientService.class,
27. AvrcpControllerService.class,
28. SapService.class,
29. HidDevService.class
30. };
31. //Resourceflag to indicate whether profile is supported or not.
32. private static final int[] PROFILE_SERVICES_FLAG = {
33. R.bool.profile_supported_hs_hfp,
34. R.bool.profile_supported_a2dp,
35. R.bool.profile_supported_a2dp_sink,
36. R.bool.profile_supported_hid,
37. R.bool.profile_supported_hdp,
38. R.bool.profile_supported_pan,
39. R.bool.profile_supported_gatt,
40. R.bool.profile_supported_map,
41. R.bool.profile_supported_hfpclient,
42. R.bool.profile_supported_avrcp_controller,
43. R.bool.profile_supported_sap,
44. R.bool.profile_supported_hidd
45. };
46.
47. private static Class[] SUPPORTED_PROFILES =new Class[0];
48.
49. static void init(Context ctx) {
50. if (ctx == null) {
51. return;
52. }
53. Resources resources =ctx.getResources();
54. if (resources == null) {
55. return;
56. }
57. ArrayList<Class> profiles = newArrayList<Class>(PROFILE_SERVICES.length);
58. for (int i=0; i <PROFILE_SERVICES_FLAG.length; i++) {
59. boolean supported =resources.getBoolean(PROFILE_SERVICES_FLAG[i]);
60. if (supported) {
61. if(!addAudioProfiles(PROFILE_SERVICES[i].getSimpleName()))
62. continue;
63. Log.d(TAG, "Adding "+ PROFILE_SERVICES[i].getSimpleName());
64. profiles.add(PROFILE_SERVICES[i]);
65. }
66. }
67. int totalProfiles = profiles.size();
68. SUPPORTED_PROFILES = newClass[totalProfiles];
69. profiles.toArray(SUPPORTED_PROFILES);
70. }
71.
72. @SuppressWarnings("rawtypes")
73. private static synchronized boolean addAudioProfiles(String serviceName) {
74. boolean isA2dpSinkEnabled =SystemProperties.getBoolean("persist.service.bt.a2dp.sink",false);
75. boolean isHfpClientEnabled =SystemProperties.getBoolean("persist.service.bt.hfp.client",false);
76. if((serviceName.equals("A2dpSinkService"))&&(!isA2dpSinkEnabled))
77. return false;
78. if((serviceName.equals("A2dpService"))&&(isA2dpSinkEnabled))
79. return false;
80.
81. if((serviceName.equals("HeadsetClientService"))&&(!isHfpClientEnabled))
82. return false;
83. if((serviceName.equals("HeadsetService"))&&(isHfpClientEnabled))
84. return false;
85.
86. return true;
87. }
88.
89. static Class[] getSupportedProfiles(){
90. return SUPPORTED_PROFILES;
91. }
92. }
注意:為了啟動HeadsetClientService服務,可以手動將布林值
isHfpClientEnabled 修改為true。
Config的init 方法呼叫 addAudioProfiles 整理出裝置支援哪些服務。
其他類呼叫getSupportedProfiles 方法就可以得到這些服務,然後啟動或者停止。
這些類都是繼承自 ProfileService,並且由AdapterService 統一管理。
3.2 啟動HeadsetClientService
在開啟藍芽的過程中, BleOnProcessStart 方法如下:
93. void BleOnProcessStart() {
94. debugLog("BleOnProcessStart()");
95. Class[] supportedProfileServices =Config.getSupportedProfiles();
96. //Initialize data objects
97. for (int i=0; i <supportedProfileServices.length;i++) {
98. mProfileServicesState.put(supportedProfileServices[i].getName(),
99. BluetoothAdapter.STATE_OFF);
100. }
101. mRemoteDevices = new RemoteDevices(this);
102. mAdapterProperties.init(mRemoteDevices);
103.
104. debugLog("BleOnProcessStart() - Make Bond State Machine");
105. mBondStateMachine = BondStateMachine.make(mPowerManager, this,
106. mAdapterProperties,mRemoteDevices);
107.
108. mJniCallbacks.init(mBondStateMachine,mRemoteDevices);
109. //Start Gatt service
110. setGattProfileServiceState(supportedProfileServices,BluetoothAdapter.STATE_ON);
111. }
會呼叫Config 的getSupportedProfiles 方法得到所支援的服務,然後在
setGattProfileServiceState 方法中逐個啟動。
112.private void setGattProfileServiceState(Class[]services, int state) {
113. if (state != BluetoothAdapter.STATE_ON && state !=BluetoothAdapter.STATE_OFF) {
114. Log.w(TAG,"setGattProfileServiceState(): invalidstate...Leaving...");
115. return;
116. }
117.
118. int expectedCurrentState= BluetoothAdapter.STATE_OFF;
119. int pendingState = BluetoothAdapter.STATE_TURNING_ON;
120.
121. if (state == BluetoothAdapter.STATE_OFF) {
122. expectedCurrentState= BluetoothAdapter.STATE_ON;
123. pendingState = BluetoothAdapter.STATE_TURNING_OFF;
124. }
125.
126. for (int i=0; i <services.length;i++) {
127. String serviceName = services[i].getName();
128. String simpleName = services[i].getSimpleName();
129. if (simpleName.equals("GattService")) {
130. Integer serviceState =mProfileServicesState.get(serviceName);
131. if(serviceState != null&& serviceState != expectedCurrentState) {
132. debugLog("setProfileServiceState()- Unable to "
133. + (state ==BluetoothAdapter.STATE_OFF ? "start" : "stop" )
134. + " service "+ serviceName
135. + ". Invalidstate: " + serviceState);
136. continue;
137. }
138. debugLog("setProfileServiceState() - "
139. + (state ==BluetoothAdapter.STATE_OFF ? "Stopping" : "Starting")
140. + " service " +serviceName);
141. mProfileServicesState.put(serviceName,pendingState);
142. Intent intent = new Intent(this,services[i]);
143. intent.putExtra(EXTRA_ACTION,ACTION_SERVICE_STATE_CHANGED);
144. intent.putExtra(BluetoothAdapter.EXTRA_STATE,state);
145. startService(intent);
146. return;
147. }
148. }
149. }
這樣,在開啟藍芽過程中, HeadsetClientService 服務就正式啟動了。
4, 藍芽通話
藍芽通話包括服務端與客戶端,服務端的主要java層程式碼路徑:
packages\apps\Bluetooth\src\com\android\bluetooth\hfpclient\
有3個類:
C/C++ 層路徑: packages\apps\Bluetooth\jni\
有一個檔案:
HeadsetClientHalConstants.java類裡面只是定義了一些int/boolean 型別的值。
HeadsetClientService.java從名字就知道它是一個服務,它的設計很有意思,裡面還有一個BluetoothHeadsetClientBinder內部類,該內部類主要負責和第三方藍芽通話apk進行跨程序通訊。HeadsetClientService即是一個服務也是
BluetoothHeadsetClientBinder和HeadsetClientStateMachine之間的橋樑。
HeadsetClientStateMachine是一個通話狀態機,即管理通話的狀態也是相關通話時java和C/C++之間的橋樑,通過JNI機制和com_android_bluetooth_hfpclient 裡面的方法互相呼叫。
com_android_bluetooth_hfpclient 藍芽通話實際動作,撥號/接聽/結束通話/拒接 實際的執行者。
客戶端主要java層程式碼路徑如下: frameworks\base\core\java\android\bluetooth\
有2個類:
BluetoothHeadsetClient.java主要負責藍芽通話的相關動作,比如接聽等等
BluetoothHeadsetClientCall.java主要負責藍芽通話的狀態,比如是來電還是去電等等。
4.1 服務端初始化
在開啟藍芽的過程中,如果裝置支援HeadsetClientService,就會啟動該服務。
然後例項化一個HeadsetClientStateMachine物件,最後對
com_android_bluetooth_hfpclient也進行初始化。
4.2 客戶端的初始化
在自己的apk中,只需要做2件事情就可以完成藍芽通話的幾乎所有動作,
1,註冊BluetoothHeadsetClientCall相關廣播,獲取藍芽電話的相關資訊,比如號碼,裝置資訊,狀態等等。
2,根據藍芽電話的相關資訊呼叫BluetoothHeadsetClient 進行相關操作。
4.2.1 註冊廣播
150.IntentFilter btfilter = new IntentFilter();
151. btfilter.addAction(BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED);
152. btfilter.addAction(BluetoothHeadsetClient.ACTION_AG_EVENT);
153. btfilter.addAction(BluetoothHeadsetClient.ACTION_CALL_CHANGED);
154. btfilter.addAction(BluetoothHeadsetClient.ACTION_AUDIO_STATE_CHANGED);
155. btfilter.addAction(BluetoothHeadsetClient.ACTION_RESULT);
156. btfilter.addAction(BluetoothHeadsetClient.ACTION_LAST_VTAG);
157. registerReceiver(mHfpClientReceiver,btfilter);
4.2.2 獲取物件
158.private final BluetoothAdaptermBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
159. privateBluetoothHeadsetClient mHfpClient = null;
160.private void enableHFP() {
161. mBluetoothAdapter.getProfileProxy(getApplicationContext(),new ServiceListener() {
162. publicvoid onServiceConnected(int profile,BluetoothProfile proxy) {
163. if(profile == BluetoothProfile.HEADSET_CLIENT) {
164. android.util.Log.d("fang", "init mBluetoothHeadset");
165. mHfpClient= (BluetoothHeadsetClient) proxy;
166. }
167. }
168. publicvoid onServiceDisconnected(int profile) {
169. if(profile == BluetoothProfile.HEADSET_CLIENT) {
170. mHfpClient= null;
171. }
172. }
173. },BluetoothProfile.HEADSET_CLIENT);
174. }
175.public boolean getProfileProxy(Contextcontext, BluetoothProfile.ServiceListener
176.listener, int profile) {
177. if (context == null || listener == null) return false;
178. if (profile == BluetoothProfile.HEADSET) {
179. BluetoothHeadset headset = new BluetoothHeadset(context, listener);
180. return true;
181. } else if (profile == BluetoothProfile.A2DP) {
182. BluetoothA2dp a2dp = new BluetoothA2dp(context, listener);
183. return true;
184. } else if (profile == BluetoothProfile.A2DP_SINK) {
185. BluetoothA2dpSink a2dpSink = new BluetoothA2dpSink(context, listener);
186. return true;
187. } else if (profile == BluetoothProfile.AVRCP_CONTROLLER) {
188. BluetoothAvrcpController avrcp = new BluetoothAvrcpController(context,
189. listener);
190. return true;
191. } else if (profile == BluetoothProfile.INPUT_DEVICE) {
192. BluetoothInputDevice iDev = new BluetoothInputDevice(context, listener);
193. return true;
194. } else if (profile == BluetoothProfile.PAN){
195. BluetoothPan pan = new BluetoothPan(context, listener);
196. return true;
197. } else if (profile == BluetoothProfile.DUN) {
198. BluetoothDun dun = new BluetoothDun(context, listener);
199. return true;
200. } else if (profile == BluetoothProfile.HEALTH) {
201. BluetoothHealth health = new BluetoothHealth(context, listener);
202. return true;
203. } else if (profile == BluetoothProfile.MAP) {
204. BluetoothMap map = new BluetoothMap(context, listener);
205. return true;
206. } else if (profile == BluetoothProfile.HEADSET_CLIENT) {
207. BluetoothHeadsetClient headsetClient = new BluetoothHeadsetClient(context, listener);
208. return true;
209. } else if (profile == BluetoothProfile.SAP) {
210. BluetoothSap sap = new BluetoothSap(context, listener);
211. return true;
212. } else if (profile == BluetoothProfile.HID_DEVICE) {
213. BluetoothHidDevice hidd = new BluetoothHidDevice(context, listener);
214. return true;
215. } else {
216. return false;
217. }
218. }
這些客戶端的類都是繼承自 BluetoothProfile典型的工廠模式,得到不同的例項物件,繼續看BluetoothHeadsetClient的建構函式
219./*package*/ BluetoothHeadsetClient(Contextcontext, ServiceListener l) {
220. mContext = context;
221. mServiceListener = l;
222. mAdapter = BluetoothAdapter.getDefaultAdapter();
223.
224. IBluetoothManager mgr = mAdapter.getBluetoothManager();
225. if (mgr != null) {
226. try {
227. mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
228. } catch (RemoteException e) {
229. Log.e(TAG,"",e);
230. }
231. }
232.
233. doBind();// 和服務端的 BluetoothHeadsetClientBinder 進行連線
234. }
235.
將客戶端的BluetoothHeadsetClient 和服務端的BluetoothHeadsetClientBinder通過Blinder機制連線起來進行通訊和呼叫。
4.2.3 連線
通過藍芽將2個裝置連線起來之後,還需要利用BluetoothHeadsetClient連線遠端裝置,打通這條路才可以進一步進行接聽結束通話等操作。
呼叫的流程從客戶端到服務端,最後到C/C++ 層實現。斷開的流程和這個完全相同。
4.3 通話操作
與通話相關的操作主要有4個:
236. mHfpClient.dial(mRemoteDevice,number); // 撥號
237. mHfpClient.acceptCall(mRemoteDevice,BluetoothHeadsetClient.CALL_ACCEPT_NONE);//接聽
238. mHfpClient.rejectCall(mRemoteDevice); // 拒接
239. mHfpClient.terminateCall(mRemoteDevice, 0);//結束通話
4個的流程以及原理都是一樣的,也和上面連線的流程是完全相同的。
240.boolean acceptCall(BluetoothDevicedevice, int flag) {
241. enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTHpermission");
242. int connectionState = mStateMachine.getConnectionState(device);
243. if (connectionState != BluetoothProfile.STATE_CONNECTED &&
244. connectionState !=BluetoothProfile.STATE_CONNECTING) {
245. return false;
246. }
247. Message msg =
248. mStateMachine.obtainMessage(HeadsetClientStateMachine.ACCEPT_CALL);//12
249. msg.arg1 = flag; // BluetoothHeadsetClient.CALL_ACCEPT_NONE = 0
250. mStateMachine.sendMessage(msg);
251. return true;
252. }
254. public final void handleMessage(Message msg) {
255. if (!mHasQuit) {
256. if (mDbg)mSm.log("handleMessage: E msg.what=" + msg.what);
257.
258. /** Save the current message */
259. mMsg = msg;
260.
261. /** State that processed themessage */
262. State msgProcessedState = null;
263. if (mIsConstructionCompleted) {
264. /** Normal path */
265. msgProcessedState = processMsg(msg);
266. } else if(!mIsConstructionCompleted && (mMsg.what == SM_INIT_CMD)
267. && (mMsg.obj ==mSmHandlerObj)) {
268. /** Initial one time path.*/
269. mIsConstructionCompleted =true;
270. invokeEnterMethods(0);
271. } else {
272. throw newRuntimeException("StateMachine.handleMessage: "
273. + "The startmethod not called, received msg: " + msg);
274. }
275. performTransitions(msgProcessedState, msg);
276.
277. // We need to check if mSm ==null here as we could be quitting.
278. if (mDbg && mSm !=null) mSm.log("handleMessage: X");
279. }
280. }
281.
282.private final State processMsg(Message msg) {
283. StateInfo curStateInfo = mStateStack[mStateStackTopIndex];
284. if (isQuit(msg)) {
285. transitionTo(mQuittingState);
286. } else {
287. while (!curStateInfo.state.processMessage(msg)) {
288. ···
289. }
290. }
291. return (curStateInfo != null) ? curStateInfo.state : null;
292. }
獲取最新的狀態,然後處理對應的訊息, HeadsetClientStateMachine 有4中狀態,分別是 Disconnected(未連線狀態), Connecting(正在連線狀態), Connected(已連線狀態), AudioOn(?)。上面已經說過,在電話的操作之前首先進行的是連線,所以現在的狀態是Connected。
除了dial 方法最後呼叫從C/C++ dialNative之外,
293.private native boolean dialNative(String number);
其它的3個方法最後都是呼叫handleCallActionNative,只是action 不同而已
294.private native boolean handleCallActionNative(int action, int index);
4.4 通話狀態
通話的狀態都是底層往上層逐級傳輸,com_android_bluetooth_hfpclient 通過onCall 方法通知java 層通話狀態的改變,通話狀態如下:
定義在BluetoothHeadsetClientCall.java 中:
295.public static final int CALL_STATE_ACTIVE =0;// 通話
296.public static final int CALL_STATE_HELD = 1;
297.public static final int CALL_STATE_DIALING =2;// 正在撥號
298.public static final int CALL_STATE_ALERTING =3;
299.public static final int CALL_STATE_INCOMING =4;// 來電
300.public static final int CALL_STATE_WAITING =5;
301.public static final intCALL_STATE_HELD_BY_RESPONSE_AND_HOLD = 6;
302.public static final int CALL_STATE_TERMINATED= 7;// 結束通話
303.private void onCall(int call) {
304. StackEvent event = new StackEvent(EVENT_TYPE_CALL);
305. event.valueInt = call;
306. Log.d(TAG, "incoming" + event);
307. sendMessage(STACK_EVENT,event);
308. }
309.private void updateCallIndicator(int call) {
310. Log.d(TAG, "updateCallIndicator " + call);
311.
312. if (waitForIndicators(call, -1, -1)) {
313. return;
314. }
315.
316. if (mQueryCallsSupported) {
317. sendMessage(QUERY_CURRENT_CALLS);
318. return;
319. }
320.
321. BluetoothHeadsetClientCall c = null;
322.
323. switch (call) {
324. case HeadsetClientHalConstants.CALL_NO_CALLS_IN_PROGRESS:// 沒有call
325. removeCalls(BluetoothHeadsetClientCall.CALL_STATE_ACTIVE,
326. BluetoothHeadsetClientCall.CALL_STATE_HELD,
327. BluetoothHeadsetClientCall.CALL_STATE_HELD_BY_RESPONSE_AND_HOLD);
328.
329. break;
330. case HeadsetClientHalConstants.CALL_CALLS_IN_PROGRESS:// 有call
331. if (mIndicatorCall ==HeadsetClientHalConstants.CALL_CALLS_IN_PROGRESS) {
332. // WP7.8 is sending call=1before setup=0 when rejecting
333. // waiting call
334. if (mIndicatorCallSetup !=HeadsetClientHalConstants.CALLSETUP_NONE) {
335. c =getCall(BluetoothHeadsetClientCall.CALL_STATE_WAITING);
336. if (c != null) {
337. setCallState(c,
338. BluetoothHeadsetClientCall.CALL_STATE_TERMINATED);
339. mCalls.remove(c.getId());
340. }
341. }
342.
343. break;
344. }
345.
346. // if there is only waitingcall it is changed to incoming so
347. // don't
348. // handle it here
349. if (mIndicatorCallSetup !=HeadsetClientHalConstants.CALLSETUP_NONE) {
350. c =getCall(BluetoothHeadsetClientCall.CALL_STATE_DIALING,
351. BluetoothHeadsetClientCall.CALL_STATE_ALERTING,
352. BluetoothHeadsetClientCall.CALL_STATE_INCOMING);
353. if (c != null) {
354. setCallState(c,BluetoothHeadsetClientCall.CALL_STATE_ACTIVE);
355. }
356. }
357.
358. updateCallsMultiParty();
359. break;
360. default:
361. break;
362. }
363.
364. mIndicatorCall = call;
365. }
366.
367.private void sendCallChangedIntent(BluetoothHeadsetClientCall c) {
368. Intent intent = new Intent(BluetoothHeadsetClient.ACTION_CALL_CHANGED);
369. intent.putExtra(BluetoothHeadsetClient.EXTRA_CALL, c);
370. mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM);
371. }
可以看到,在onCall 方法中,獲取不同的通話狀態,最後都會通過
sendCallChangedIntent 方法傳送廣播,將通話的狀態傳送出去,我們可以註冊該廣播並處理對應的通話狀態。
相關推薦
藍芽通話功能原始碼解析
3 藍芽耳機服務 在開啟藍芽過程中,會開啟一些對應的服務,在此只將和通話相關的一個服務, HeadsetClientService。手機上只有開啟了這個服務,才可以將該手機當作一個藍芽耳機,通話時聲音才可以傳輸過來。 3.1 準備 首先在啟動apk時,會首先啟動該apk的A
藍芽map協議原始碼解析
MAP協議 使用場景:智慧車載中同步簡訊,彩信等資訊 1 協議概述 協議程式碼路徑: frameworks\opt\bluetooth\src\android\bluetooth\client\ map這個包中 Jar包名稱,android.bluetooth.cli
藍芽pbap協議原始碼解析
PBAP協議 使用場景:智慧車載中同步聯絡人等資訊 其實,不僅可以同步聯絡人,還可以同步通話記錄等資訊。 1.協議概述 協議程式碼路徑: frameworks\opt\bluetooth\src\android\bluetooth\client\pbap這個包中 Jar
藍芽通話機制原理
[摘要]: 本文主要論述基於android 6.0的藍芽上層(Java層)通話機制;總結了藍芽通話框架,並且給出了接聽電話的詳細的流程圖;最後說明了apk的實現以及總結了藍芽/android 相關的知識點。 1, 藍芽框架 主要程式碼路徑: 路徑1: frameworks\
android 藍芽搜尋功能實現
MainActivity.java package com.example.bluefind; import android.media.AudioManager; import android.os.Bundle; import android.app.Activit
Bluedroid: 藍芽協議棧原始碼剖析
一、 基礎知識介紹 1.縮略語 BTIF: Bluetooth Interface BTU : Bluetooth Upper Layer BTM: Bluetooth Manager BTE: Bluetooth embedded system
51822藍芽協議之例項解析L2CAP協議
概要: 邏輯鏈路控制和適配協議(L2CAP),支援高層協議多路複用、資料分段和重組,並且支援傳送服務質量資訊。 本檔案主要針對協議狀態自動機、分組格式及構成相關內容進行描述 一、下面介紹L2CA
SpringBoot之@ConfigurationProperties自動注入成員變數值功能原始碼解析
前言: 我們在使用SpringBoot開發相關專案時,經常會使用到@ConfigurationProperties註解,這個註解配合application.properties(或yml檔案)使用,可以快速的為我們的Bean的成員變數進行賦值,常規用法如下: //
ANDROID藍芽4.0開發_藍芽開發例項原始碼下載
一個整合目前主流藍芽的demo、android藍芽4.0開發、專案中使用到了搜尋BLE終端和讀寫BLE終端、另外還有在程式裡面開房藍芽、在Demo裡面還處理了收到BLE終端資料互動的事件、程式碼裡都有中文註釋、要讀性應該非常好的、應該會對開發有一定幫助、有興趣的哥們可以下
藍芽通話鏈路和手機通話鏈路有區別麼?
近日友人推了一個部落格:https://blog.csdn.net/david_tym/article/details/80963732 作者對手機中通話鏈路進行了詳細的分析。其中包括了經典的CP Call(運營商提供的電話業務)和流行的AP Call(VoIP等OTT業務)的鏈路,值得一看。 不過在藍芽耳機
GPS、藍芽、解析字串等功能
主體佈局 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="htt
藍芽檔案傳輸之obex層之上的分析【Android原始碼解析】
在上節中我們仔細分析了藍芽檔案傳輸過程中涉及到的UI介面,最終定格在藍芽裝置掃描的介面,我們只要選擇自己想要傳輸的藍芽裝置就可以進行藍芽檔案的傳輸了。那就是這樣一個簡單的裝置選擇的點選會引發哪些
藍芽解析(3):BLE協議棧解析
轉自http://www.wowotech.net/bluetooth/ble_stack_overview.html 1. 前言 本文從協議棧設計者的角度,思考如下問題: 為什麼會有藍芽協議棧(Why)? 怎樣實現藍芽協議棧(How)? 藍芽協議棧的最終樣子是什麼(
藍芽解析(2):協議架構分析
轉自http://www.wowotech.net/bluetooth/bt_protocol_arch.html 1. 前言 本文是藍芽解析的第二篇文章,在part1的基礎上,從整體架構的角度,瞭解藍芽協議的組成,以便加深對藍芽的理解。 2. 協議層
藍芽解析(1):藍芽的基本概念
2. 藍芽技術的概述 2.1 兩種藍芽技術:Basic Rate(BR)和Low Energy(LE) 藍芽協議包括兩種技術:Basic Rate(簡稱BR)和Low Energy(簡稱LE)。這兩種技術,都包括搜尋(discovery)管理、連線(connection)管
藍芽學習筆記之例項廣播資料的解析
轉載自:https://blog.csdn.net/sinat_23338865/article/details/52170581 BLE 裝置工作的第一步就是向外廣播資料。廣播資料中帶有裝置相關的資訊。 本文主要說一下 BLE 的廣播中的資料的規範以及廣播包的解析,這是我們專案中的廣播資料
藍芽BLE V4.2, V5 新功能介紹
注:此文摘抄自 http://www.sunyouqun.com/category/ble-stack/ 協議釋出時間 協議版本 2016/12 Bluetooth 5 20
藍芽BLE---DA14683的IIC主機通訊C原始碼
demo_i2c.h /* * demo_i2c.h * * Created on: 2018年12月7日 * Author: Jim */ #ifndef SDK_PERIPHERALS_INCLUDE_DEMO_I2C_H_ #define SDK_PERIPHE
在Linux系統中使用藍芽功能的基本方法
首先確定硬體上有支援藍芽的裝置,然後執行如下命令,就可以開到我們的藍芽裝置了: lsusb 執行hciconfig可以看到: 從上圖可以看出,我們的藍芽裝置是hci0 執行hcitool dev可以看到我們的藍芽裝置的硬體地址 執行hcitoo --
mini3d原始碼解析及功能擴充套件
簡介 mini3d是前網易員工@韋易笑開發的3d軟渲染引擎,總程式碼量不到1000行,短小精悍,適合初學者學習。 本文結合原始碼給出自己的理解,並在原作基礎上實現功能擴充套件: 補充缺少的三維變換功能(平移、縮放) 增加簡單光照(漫反射) 程式碼簡析 這個