1. PBAP/PCE/SSM/BV-10-C
[PCE Does not share PbapSupportedFeatures bits]
這個 PTS 測試用例 PBAP/PCE/SSM/BV-10-C
的核心目的是驗證 PBAP 客戶端(PCE)在與舊版服務器通信時,不會發送 PbapSupportedFeatures 特性位,以確保兼容性和標準符合性。
1. 測試目標
Verify that the PCE does not share its PbapSupportedFeatures bits with a legacy server.
目的:驗證 PCE(Phonebook Client Equipment)在與一個不支持 PbapSupportedFeatures SDP 屬性的 legacy(傳統)PBAP Server 建立連接時,不應在 OBEX CONNECT 請求中包含 PbapSupportedFeatures Header。
這是為了保證新版本設備與老版本設備之間的向后兼容性。
2. 參考
[PBAP 1.2] 6.3節: 明確指出當 Server 的 SDP 中沒有 PbapSupportedFeatures 屬性時,Client 不應在 OBEX CONNECT 請求中發送該 Header 字段。
3. 測試條件
-
The IUT and the Lower Tester have been paired.
-
Lower Tester: The Lower Tester is in discoverable and connectable mode. The Lower Tester
does not have a PbapSupportedFeatures attribute in its SDP record. -
IUT(被測設備)和 Lower Tester 已配對。
-
Lower Tester 是 PBAP Server,并處于:
- Discoverable + Connectable 狀態
- 其 SDP 記錄中不含 PbapSupportedFeatures 屬性
4. 測試流程
- Lower Tester 啟動并廣播自己的 SDP 信息(無 PbapSupportedFeatures 屬性)。
- IUT(PCE) 嘗試發起 PBAP 連接。
- 觀察 IUT 的 OBEX CONNECT 請求內容。
5.預期流程
OBEX CONNECT 請求中:
- 不得包含 PbapSupportedFeatures Header(0xFB)
- 若包含此字段,則視為 FAIL
6.如何判斷測試是否通過
抓包(如使用 Wireshark + Bluetooth HCI log)時,查看:
- OBEX CONNECT 請求幀中是否包含 header ID 為
0xFB
- 如果沒有,則測試通過
2. 真實測試案例
Test case : PBAP/PCE/SSM/BV-10-C started
- version=0x0102 rfcommPsm=0x02 l2capPsm=0x1005 supportRepositories=0x0f supportedFeatures=0x00000000
- Final supported Feature 0
- OBEX Connect request contains SupportedFeatures ApplicationParameter
- Received HCI disconnection event. Handle = 0x0063-Final Verdict: FAIL
PBAP/PCE/SSM/BV-10-C finished
1. pts 側
- 車機向 PTS 發起了 SDP
- 但是 PTS SDP中回復了兩個 AttributeList
第一個 Attribute List如下
- 支持協議 V1.2
- 可以通過 l2cap psm: 0x1005 對應十進制:4101
- 支持 supported features.
- 可以通過 RFCOMM 通道 2 連接
第二個 Attribute List 如下
- 支持協議 v.1.0
- 可以通過 RFCOMM 通道 2 連接
這個 PTS 測試用例 PBAP/PCE/SSM/BV-10-C
的核心目的是驗證 PBAP 客戶端(PCE)在與舊版服務器通信時,不會發送 PbapSupportedFeatures 特性位,以確保兼容性和標準符合性。
- 也就是說 車機應該去連接 V1.0 這個。
2. 車機側
# 車機收到第一個 UUID 也就是 v1.2
04-25 12:04:14.238917 6075 6075 I PbapClientStateMachine: Received UUID: 0000112f-0000-1000-8000-00805f9b34fb
04-25 12:04:14.239812 6075 6498 I PbapClientConnHandler: Handling Message = 1
# 使用 l2cap psm 0x1005 已經去連接了
04-25 12:04:14.239949 6075 6498 V PbapClientConnHandler: connectSocket: PSM: 410104-25 12:04:14.321776 6075 6498 D PbapClientConnHandler: Socket connected
04-25 12:04:14.321849 6075 6498 V PbapClientConnHandler: Start Obex Client Session
04-25 12:04:14.327761 6075 6498 D PbapClientConnHandler: Remote PbapSupportedFeatures 0
04-25 12:04:14.373370 6075 6498 D PbapClientConnHandler: Success = true# 車機收到 第二個 UUID v1.0
04-25 12:04:14.239991 6075 6075 I PbapClientStateMachine: Received UUID: 0000112f-0000-1000-8000-00805f9b34fb04-25 12:04:14.373594 6075 6498 I PbapClientConnHandler: Handling Message = 1# 使用 rfcomm channle 2 去連, 連失敗了。
04-25 12:04:14.373891 6075 6498 V PbapClientConnHandler: connectSocket: channel: 2
04-25 12:04:14.471800 6075 6498 E PbapClientConnHandler: Error while connecting socket
04-25 12:04:14.471800 6075 6498 E PbapClientConnHandler: java.io.IOException: read failed, socket might closed or timeout, read ret: -1
- 其實第一次 使用 v1.2 去連的時候,已經下發了 PbapSupportedFeatures , 所以 測試失敗了。
3. 解決辦法
通過上面的分析,我們可以清晰的看到, 車機在這個測試中,不應該處理 v1.2 的連接。 應該去連接 v1.0
- 那我們就按照這個思路來做調整。
3. 代碼分析
// android/app/src/com/android/bluetooth/pbapclient/PbapClientConnectionHandler.java@Overridepublic void handleMessage(Message msg) {if (DBG) {Log.i(TAG, "Handling Message = " + msg.what);}switch (msg.what) {case MSG_CONNECT:mRetryTime = 0;case MSG_RECONNECT:mPseRec = (SdpPseRecord) msg.obj; // 將 SDP 賦值給 mPseRec/* To establish a connection, first open a socket and then create an OBEX session */if (connectSocket()) {} else {return;}if (connectObexSession()) {} else {}break;}return;}
整個 連接邏輯其實很簡單,當SDP 結束后,會觸發 PbapClientConnectionHandler 處理 MSG_CONNECT 消息。
- 調用 connectSocket()
- 如果第一步調用成功,調用 connectObexSession()
synchronized boolean connectSocket() { try {/* Use BluetoothSocket to connect */if (mPseRec == null) {// BackWardCompatability: Fall back to create RFCOMM through UUID.if (VDBG) Log.v(TAG, "connectSocket: UUID: " + BluetoothUuid.PBAP_PSE.getUuid());mSocket =mDevice.createRfcommSocketToServiceRecord(BluetoothUuid.PBAP_PSE.getUuid());} else if (mPseRec.getL2capPsm() != L2CAP_INVALID_PSM) { // 由于 V1.2 中 L2cap 的 PCM 是 0x1005 對應十進制:4101 , 這里有限去連接 v1.2 if (SystemProperties.getBoolean("xxx.bluetooth.pts.pbap.pce.ssm.bv-10-c", false)) {Log.d(TAG, "pts test, pbap.pce.ssm.bv-10-c");} else {if (VDBG)Log.v(TAG, "connectSocket: PSM: " + mPseRec.getL2capPsm());mSocket = mDevice.createL2capSocket(mPseRec.getL2capPsm()); // 也就是 v1.2 是使用 l2cap 通道去 連接 obex 的。}} else {// 這里是 v1.0 的邏輯, 使用 rfcomm channel:2 去連接 obexif (VDBG) Log.v(TAG, "connectSocket: channel: " + mPseRec.getRfcommChannelNumber());mSocket = mDevice.createRfcommSocket(mPseRec.getRfcommChannelNumber());}if (mSocket != null) {mSocket.connect();return true;} else {Log.w(TAG, "Could not create socket");}} catch (IOException e) {Log.e(TAG, "Error while connecting socket", e);}return false;}
-
上面已經給出了處理辦法,就是在當前測試環境中,不去連 v1.2 協議。
-
第一步,只是 決定了 當前 上層obex 走那種通道, 是直接走 l2cap, 還是 走 rfcomm->l2cap 方式
那還有一個疑問, 測試提到的 PbapSupportedFeatures 是在哪里設置的呢?
繼續看 第二步 connectObexSession
boolean connectObexSession() {boolean connectionSuccessful = false;try {if (VDBG) {Log.v(TAG, "Start Obex Client Session");}BluetoothObexTransport transport = new BluetoothObexTransport(mSocket);mObexSession = new ClientSession(transport);mObexSession.setAuthenticator(mAuth);HeaderSet connectionRequest = new HeaderSet();connectionRequest.setHeader(HeaderSet.TARGET, PBAP_TARGET);if (mPseRec != null) {if (DBG) {Log.d(TAG, "Remote PbapSupportedFeatures " + mPseRec.getSupportedFeatures());}ObexAppParameters oap = new ObexAppParameters();if (mPseRec.getProfileVersion() >= PBAP_V1_2) { // 如何是 v1.2 就會去添加 PbapSupportedFeatures, 如何是 1.0 跳過oap.add(BluetoothPbapRequest.OAP_TAGID_PBAP_SUPPORTED_FEATURES,PBAP_SUPPORTED_FEATURE);}oap.addToHeaderSet(connectionRequest);}HeaderSet connectionResponse = mObexSession.connect(connectionRequest); // 這里會去發起 obex 的連接請求connectionSuccessful =(connectionResponse.getResponseCode() == ResponseCodes.OBEX_HTTP_OK);if (DBG) {Log.d(TAG, "Success = " + Boolean.toString(connectionSuccessful));}} return connectionSuccessful;}
4.整改后的
# 收到 sdp
04-25 13:22:04.301633 5766 5766 I PbapClientStateMachine: Received UUID: 0000112f-0000-1000-8000-00805f9b34fb
04-25 13:22:04.302432 5766 5766 I PbapClientStateMachine: Received UUID: 0000112f-0000-1000-8000-00805f9b34fb# v1.2 連接失敗
04-25 13:22:04.303335 5766 6197 I PbapClientConnHandler: Handling Message = 1
04-25 13:22:04.303911 5766 6197 D PbapClientConnHandler: pts test, pbap.pce.ssm.bv-10-c
04-25 13:22:04.304104 5766 6197 W PbapClientConnHandler: Could not create socket
04-25 13:22:04.304145 5766 6197 W PbapClientConnHandler: Socket CONNECT Failure # v1.0 連接成功
04-25 13:22:04.304193 5766 6197 I PbapClientConnHandler: Handling Message = 1
04-25 13:22:04.304238 5766 6197 V PbapClientConnHandler: connectSocket: channel: 2
04-25 13:22:04.419481 5766 6197 D PbapClientConnHandler: Socket connected
04-25 13:22:04.419564 5766 6197 V PbapClientConnHandler: Start Obex Client Session
04-25 13:22:04.425265 5766 6197 D PbapClientConnHandler: Remote PbapSupportedFeatures 0
04-25 13:22:04.483244 5766 6197 D PbapClientConnHandler: Success = true
04-25 13:22:04.483363 5766 6197 I PbapClientConnHandler: Handling Message = 257
04-25 13:22:04.483404 5766 6197 D PbapClientConnHandler: pts test, pbap.pce.ssm.bv-10-c