1 /* 2 * Copyright (C) 2014 Samsung System LSI 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.bluetooth.tests; 18 19 import java.io.IOException; 20 import java.io.PipedInputStream; 21 import java.io.PipedOutputStream; 22 import java.util.ArrayList; 23 import java.util.concurrent.CountDownLatch; 24 25 import javax.obex.HeaderSet; 26 import javax.obex.ObexTransport; 27 import javax.obex.Operation; 28 import javax.obex.ResponseCodes; 29 import javax.obex.ServerRequestHandler; 30 31 import junit.framework.Assert; 32 import android.annotation.TargetApi; 33 import android.bluetooth.BluetoothAdapter; 34 import android.bluetooth.BluetoothDevice; 35 import android.bluetooth.BluetoothServerSocket; 36 import android.bluetooth.BluetoothSocket; 37 import android.bluetooth.BluetoothUuid; 38 import android.bluetooth.SdpMasRecord; 39 import android.content.BroadcastReceiver; 40 import android.content.Context; 41 import android.content.Intent; 42 import android.content.IntentFilter; 43 import android.net.LocalServerSocket; 44 import android.net.LocalSocket; 45 import android.os.Build; 46 import android.os.Debug; 47 import android.os.ParcelUuid; 48 import android.test.AndroidTestCase; 49 import android.util.Log; 50 51 import com.android.bluetooth.BluetoothObexTransport; 52 import com.android.bluetooth.sdp.SdpManager; 53 import com.android.bluetooth.tests.TestSequencer.OPTYPE; 54 55 /** 56 * Test either using the reference ril without a modem, or using a RIL implementing the 57 * BT SAP API, by providing the rild-bt socket as well as the extended API functions for SAP. 58 * 59 */ 60 @TargetApi(Build.VERSION_CODES.KITKAT) 61 public class ObexTest extends AndroidTestCase implements ITestSequenceConfigurator { 62 protected static String TAG = "ObexTest"; 63 protected static final boolean D = true; 64 protected static final boolean TRACE = false; 65 protected static final boolean DELAY_PASS_30_SEC = false; 66 public static final long PROGRESS_INTERVAL_MS = 1000; 67 private static final ObexTestParams defaultParams = 68 new ObexTestParams(2*8092, 0, 2*1024*1024); 69 70 private static final ObexTestParams throttle100Params = 71 new ObexTestParams(2*8092, 100000, 1024*1024); 72 73 private static final ObexTestParams smallParams = 74 new ObexTestParams(2*8092, 0, 2*1024); 75 76 private static final ObexTestParams hugeParams = 77 new ObexTestParams(2*8092, 0, 100*1024*1024); 78 79 private static final int SMALL_OPERATION_COUNT = 1000; 80 private static final int CONNECT_OPERATION_COUNT = 4500; 81 82 private static final int L2CAP_PSM = 29; /* If SDP is not used */ 83 private static final int RFCOMM_CHANNEL = 29; /* If SDP is not used */ 84 85 //public static final String SERVER_ADDRESS = "10:68:3F:5E:F9:2E"; 86 public static final String SERVER_ADDRESS = "F8:CF:C5:A8:70:7E"; 87 88 private static final String SDP_SERVER_NAME = "Samsung Server"; 89 private static final String SDP_CLIENT_NAME = "Samsung Client"; 90 91 private static final long SDP_FEATURES = 0x87654321L; /* 32 bit */ 92 private static final int SDP_MSG_TYPES = 0xf1; /* 8 bit */ 93 private static final int SDP_MAS_ID = 0xCA; /* 8 bit */ 94 private static final int SDP_VERSION = 0xF0C0; /* 16 bit */ 95 public static final ParcelUuid SDP_UUID_OBEX_MAS = BluetoothUuid.MAS; 96 97 private static int sSdpHandle = -1; 98 private static final ObexTestDataHandler sDataHandler = new ObexTestDataHandler("(Client)"); 99 private static final ISeqStepValidator sResponseCodeValidator = new ResponseCodeValidator(); 100 private static final ISeqStepValidator sDataValidator = new DataValidator(); 101 102 103 private enum SequencerType { 104 SEQ_TYPE_PAYLOAD, 105 SEQ_TYPE_CONNECT_DISCONNECT 106 } 107 108 private Context mContext = null; 109 private int mChannelType = 0; 110 ObexTest()111 public ObexTest() { 112 super(); 113 } 114 115 /** 116 * Test that a connection can be established. 117 * WARNING: The performance of the pipe implementation is not good. I'm only able to get a 118 * throughput of around 220 kbyte/sec - less that when using Bluetooth :-) 119 * UPDATE: Did a local socket implementation below to replace this... 120 * This has a throughput of more than 4000 kbyte/s 121 */ testLocalPipes()122 public void testLocalPipes() { 123 mContext = this.getContext(); 124 System.out.println("Setting up pipes..."); 125 126 PipedInputStream clientInStream = null; 127 PipedOutputStream clientOutStream = null; 128 PipedInputStream serverInStream = null; 129 PipedOutputStream serverOutStream = null; 130 ObexPipeTransport clientTransport = null; 131 ObexPipeTransport serverTransport = null; 132 133 try { 134 /* Create and interconnect local pipes for transport */ 135 clientInStream = new PipedInputStream(5*8092); 136 clientOutStream = new PipedOutputStream(); 137 serverInStream = new PipedInputStream(clientOutStream, 5*8092); 138 serverOutStream = new PipedOutputStream(clientInStream); 139 140 /* Create the OBEX transport objects to wrap the pipes - enable SRM */ 141 clientTransport = new ObexPipeTransport(clientInStream, clientOutStream, true); 142 serverTransport = new ObexPipeTransport(serverInStream, serverOutStream, true); 143 144 TestSequencer sequencer = createBtPayloadTestSequence(clientTransport, serverTransport); 145 146 //Debug.startMethodTracing("ObexTrace"); 147 assertTrue(sequencer.run(mContext)); 148 //Debug.stopMethodTracing(); 149 } catch (IOException e) { 150 Log.e(TAG, "IOException", e); 151 } 152 } 153 154 /** 155 * Run the test sequence using a local socket. 156 * Throughput around 4000 kbyte/s - with a larger OBEX package size. 157 */ testLocalSockets()158 public void testLocalSockets() { 159 mContext = this.getContext(); 160 System.out.println("Setting up sockets..."); 161 162 try { 163 /* Create and interconnect local pipes for transport */ 164 LocalServerSocket serverSock = new LocalServerSocket("com.android.bluetooth.tests.sock"); 165 LocalSocket clientSock = new LocalSocket(); 166 LocalSocket acceptSock; 167 168 clientSock.connect(serverSock.getLocalSocketAddress()); 169 170 acceptSock = serverSock.accept(); 171 172 /* Create the OBEX transport objects to wrap the pipes - enable SRM */ 173 ObexPipeTransport clientTransport = new ObexPipeTransport(clientSock.getInputStream(), 174 clientSock.getOutputStream(), true); 175 ObexPipeTransport serverTransport = new ObexPipeTransport(acceptSock.getInputStream(), 176 acceptSock.getOutputStream(), true); 177 178 TestSequencer sequencer = createBtPayloadTestSequence(clientTransport, serverTransport); 179 180 //Debug.startMethodTracing("ObexTrace"); 181 assertTrue(sequencer.run(mContext)); 182 //Debug.stopMethodTracing(); 183 184 clientSock.close(); 185 acceptSock.close(); 186 serverSock.close(); 187 } catch (IOException e) { 188 Log.e(TAG, "IOException", e); 189 } 190 } 191 192 /* Create a sequence of put/get operations with different payload sizes */ createBtPayloadTestSequence(ObexTransport clientTransport, ObexTransport serverTransport)193 private TestSequencer createBtPayloadTestSequence(ObexTransport clientTransport, 194 ObexTransport serverTransport) 195 throws IOException { 196 TestSequencer sequencer = new TestSequencer(clientTransport, serverTransport, this); 197 SeqStep step; 198 199 step = sequencer.addStep(OPTYPE.CONNECT, sResponseCodeValidator); 200 if(false){ 201 202 step = sequencer.addStep(OPTYPE.PUT, sDataValidator); 203 step.mParams = defaultParams; 204 step.mUseSrm = true; 205 206 step = sequencer.addStep(OPTYPE.GET, sDataValidator); 207 step.mParams = defaultParams; 208 step.mUseSrm = true; 209 210 step = sequencer.addStep(OPTYPE.PUT, sDataValidator); 211 step.mParams = throttle100Params; 212 step.mUseSrm = true; 213 214 step = sequencer.addStep(OPTYPE.GET, sDataValidator); 215 step.mParams = throttle100Params; 216 step.mUseSrm = true; 217 218 for(int i=0; i<SMALL_OPERATION_COUNT; i++){ 219 step = sequencer.addStep(OPTYPE.PUT, sDataValidator); 220 step.mParams = smallParams; 221 step.mUseSrm = true; 222 223 step = sequencer.addStep(OPTYPE.GET, sDataValidator); 224 step.mParams = smallParams; 225 step.mUseSrm = true; 226 227 } 228 } 229 230 step = sequencer.addStep(OPTYPE.PUT, sDataValidator); 231 step.mParams = hugeParams; 232 step.mUseSrm = true; 233 234 step = sequencer.addStep(OPTYPE.GET, sDataValidator); 235 step.mParams = hugeParams; 236 step.mUseSrm = true; 237 step = sequencer.addStep(OPTYPE.DISCONNECT, sResponseCodeValidator); 238 239 return sequencer; 240 } 241 createBtConnectTestSequence(ObexTransport clientTransport, ObexTransport serverTransport)242 private TestSequencer createBtConnectTestSequence(ObexTransport clientTransport, 243 ObexTransport serverTransport) 244 throws IOException { 245 TestSequencer sequencer = new TestSequencer(clientTransport, serverTransport, this); 246 SeqStep step; 247 248 step = sequencer.addStep(OPTYPE.CONNECT, sResponseCodeValidator); 249 250 step = sequencer.addStep(OPTYPE.PUT, sDataValidator); 251 step.mParams = smallParams; 252 step.mUseSrm = true; 253 254 step = sequencer.addStep(OPTYPE.GET, sDataValidator); 255 step.mParams = smallParams; 256 step.mUseSrm = true; 257 258 step = sequencer.addStep(OPTYPE.DISCONNECT, sResponseCodeValidator); 259 260 return sequencer; 261 } 262 263 264 /** 265 * Use this validator to validate operation response codes. E.g. for OBEX CONNECT and 266 * DISCONNECT operations. 267 * Expects HeaderSet to be valid, and Operation to be null. 268 */ getResponsecodevalidator()269 public static ISeqStepValidator getResponsecodevalidator() { 270 return sResponseCodeValidator; 271 } 272 273 /** 274 * Use this validator to validate (and read/write data) for OBEX PUT and GET operations. 275 * Expects Operation to be valid, and HeaderSet to be null. 276 */ getDatavalidator()277 public static ISeqStepValidator getDatavalidator() { 278 return sDataValidator; 279 } 280 281 /** 282 * Use this validator to validate operation response codes. E.g. for OBEX CONNECT and 283 * DISCONNECT operations. 284 * Expects HeaderSet to be valid, and Operation to be null. 285 */ 286 private static class ResponseCodeValidator implements ISeqStepValidator { 287 validateHeaderSet(HeaderSet headers, HeaderSet expected)288 protected static boolean validateHeaderSet(HeaderSet headers, HeaderSet expected) 289 throws IOException { 290 if(headers.getResponseCode() != ResponseCodes.OBEX_HTTP_OK) { 291 Log.e(TAG,"Wrong ResponseCode: " + headers.getResponseCode()); 292 Assert.assertTrue(false); 293 return false; 294 } 295 return true; 296 } 297 298 @Override validate(SeqStep step, HeaderSet response, Operation op)299 public boolean validate(SeqStep step, HeaderSet response, Operation op) 300 throws IOException { 301 if(response == null) { 302 if(op.getResponseCode() != ResponseCodes.OBEX_HTTP_OK) { 303 Log.e(TAG,"Wrong ResponseCode: " + op.getResponseCode()); 304 Assert.assertTrue(false); 305 return false; 306 } 307 return true; 308 } 309 return validateHeaderSet(response, step.mResHeaders); 310 } 311 } 312 313 /** 314 * Use this validator to validate (and read/write data) for OBEX PUT and GET operations. 315 * Expects Operation to ve valid, and HeaderSet to be null. 316 */ 317 private static class DataValidator implements ISeqStepValidator { 318 @Override validate(SeqStep step, HeaderSet notUsed, Operation op)319 public boolean validate(SeqStep step, HeaderSet notUsed, Operation op) 320 throws IOException { 321 Assert.assertNotNull(op); 322 if(step.mType == OPTYPE.GET) { 323 op.noBodyHeader(); 324 sDataHandler.readData(op.openDataInputStream(), step.mParams); 325 } else if (step.mType == OPTYPE.PUT) { 326 sDataHandler.writeData(op.openDataOutputStream(), step.mParams); 327 } 328 int responseCode = op.getResponseCode(); 329 Log.i(TAG, "response code: " + responseCode); 330 HeaderSet response = op.getReceivedHeader(); 331 ResponseCodeValidator.validateHeaderSet(response, step.mResHeaders); 332 op.close(); 333 return true; 334 } 335 } 336 testBtServerL2cap()337 public void testBtServerL2cap() { 338 testBtServer(BluetoothSocket.TYPE_L2CAP, false, SequencerType.SEQ_TYPE_PAYLOAD); 339 } 340 testBtServerRfcomm()341 public void testBtServerRfcomm() { 342 testBtServer(BluetoothSocket.TYPE_RFCOMM, false, SequencerType.SEQ_TYPE_PAYLOAD); 343 } 344 testBtClientL2cap()345 public void testBtClientL2cap() { 346 testBtClient(BluetoothSocket.TYPE_L2CAP, false, SequencerType.SEQ_TYPE_PAYLOAD); 347 } 348 testBtClientRfcomm()349 public void testBtClientRfcomm() { 350 testBtClient(BluetoothSocket.TYPE_RFCOMM, false, SequencerType.SEQ_TYPE_PAYLOAD); 351 } 352 testBtServerSdpL2cap()353 public void testBtServerSdpL2cap() { 354 testBtServer(BluetoothSocket.TYPE_L2CAP, true, SequencerType.SEQ_TYPE_PAYLOAD); 355 } 356 testBtServerSdpRfcomm()357 public void testBtServerSdpRfcomm() { 358 testBtServer(BluetoothSocket.TYPE_RFCOMM, true, SequencerType.SEQ_TYPE_PAYLOAD); 359 } 360 testBtClientSdpL2cap()361 public void testBtClientSdpL2cap() { 362 testBtClient(BluetoothSocket.TYPE_L2CAP, true, SequencerType.SEQ_TYPE_PAYLOAD); 363 } 364 testBtClientSdpRfcomm()365 public void testBtClientSdpRfcomm() { 366 testBtClient(BluetoothSocket.TYPE_RFCOMM, true, SequencerType.SEQ_TYPE_PAYLOAD); 367 } 368 testBtServerConnectL2cap()369 public void testBtServerConnectL2cap() { 370 for(int i=0; i<CONNECT_OPERATION_COUNT; i++){ 371 Log.i(TAG, "Starting iteration " + i); 372 testBtServer(BluetoothSocket.TYPE_L2CAP, true, 373 SequencerType.SEQ_TYPE_CONNECT_DISCONNECT); 374 try { 375 Thread.sleep(50); 376 } catch (InterruptedException e) { 377 Log.e(TAG,"Exception while waiting...",e); 378 } 379 } 380 } 381 testBtClientConnectL2cap()382 public void testBtClientConnectL2cap() { 383 for(int i=0; i<CONNECT_OPERATION_COUNT; i++){ 384 Log.i(TAG, "Starting iteration " + i); 385 testBtClient(BluetoothSocket.TYPE_L2CAP, true, 386 SequencerType.SEQ_TYPE_CONNECT_DISCONNECT); 387 try { 388 // We give the server 100ms to allow adding SDP record 389 Thread.sleep(150); 390 } catch (InterruptedException e) { 391 Log.e(TAG,"Exception while waiting...",e); 392 } 393 } 394 } 395 testBtServerConnectRfcomm()396 public void testBtServerConnectRfcomm() { 397 for(int i=0; i<CONNECT_OPERATION_COUNT; i++){ 398 Log.i(TAG, "Starting iteration " + i); 399 testBtServer(BluetoothSocket.TYPE_RFCOMM, true, 400 SequencerType.SEQ_TYPE_CONNECT_DISCONNECT); 401 try { 402 Thread.sleep(50); 403 } catch (InterruptedException e) { 404 Log.e(TAG,"Exception while waiting...",e); 405 } 406 } 407 } 408 testBtClientConnectRfcomm()409 public void testBtClientConnectRfcomm() { 410 for(int i=0; i<CONNECT_OPERATION_COUNT; i++){ 411 Log.i(TAG, "Starting iteration " + i); 412 testBtClient(BluetoothSocket.TYPE_RFCOMM, true, 413 SequencerType.SEQ_TYPE_CONNECT_DISCONNECT); 414 try { 415 // We give the server 100ms to allow adding SDP record 416 Thread.sleep(250); 417 } catch (InterruptedException e) { 418 Log.e(TAG,"Exception while waiting...",e); 419 } 420 } 421 } 422 423 /** 424 * Create a serverSocket 425 * @param type 426 * @param useSdp 427 * @return 428 * @throws IOException 429 */ createServerSocket(int type, boolean useSdp)430 public static BluetoothServerSocket createServerSocket(int type, boolean useSdp) 431 throws IOException { 432 int rfcommChannel = -1; 433 int l2capPsm = -1; 434 435 BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter(); 436 if(bt == null) { 437 Log.e(TAG,"No Bluetooth Device!"); 438 assertTrue(false); 439 } 440 BluetoothTestUtils.enableBt(bt); 441 BluetoothServerSocket serverSocket=null; 442 if(type == BluetoothSocket.TYPE_L2CAP) { 443 if(useSdp == true) { 444 serverSocket = bt.listenUsingL2capOn( 445 BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP); 446 } else { 447 serverSocket = bt.listenUsingL2capOn(L2CAP_PSM); 448 } 449 l2capPsm = serverSocket.getChannel(); 450 Log.d(TAG, "L2CAP createde, PSM: " + l2capPsm); 451 } else if(type == BluetoothSocket.TYPE_RFCOMM) { 452 if(useSdp == true) { 453 serverSocket = bt.listenUsingInsecureRfcommOn( 454 BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP); 455 } else { 456 serverSocket = bt.listenUsingInsecureRfcommOn(RFCOMM_CHANNEL); 457 } 458 rfcommChannel = serverSocket.getChannel(); 459 Log.d(TAG, "RFCOMM createde, Channel: " + rfcommChannel); 460 } else { 461 fail("Invalid transport type!"); 462 } 463 if(useSdp == true) { 464 /* We use the MAP service record to be able to set rfcomm and l2cap channels */ 465 // TODO: We need to free this 466 if(sSdpHandle >= 0) { 467 SdpManager.getDefaultManager().removeSdpRecord(sSdpHandle); 468 } 469 Log.d(TAG, "Creating record with rfcomm channel: " + rfcommChannel + 470 " and l2cap channel: " + l2capPsm); 471 sSdpHandle = SdpManager.getDefaultManager().createMapMasRecord(SDP_SERVER_NAME, 472 SDP_MAS_ID, rfcommChannel, l2capPsm, 473 SDP_VERSION, SDP_MSG_TYPES, (int)(SDP_FEATURES & 0xffffffff)); 474 } else { 475 Log.d(TAG, "SKIP creation of record with rfcomm channel: " + rfcommChannel + 476 " and l2cap channel: " + l2capPsm); 477 } 478 return serverSocket; 479 } 480 removeSdp()481 public static void removeSdp() { 482 if(sSdpHandle > 0) { 483 SdpManager.getDefaultManager().removeSdpRecord(sSdpHandle); 484 sSdpHandle = -1; 485 } 486 } 487 488 /** 489 * Server side of a two device Bluetooth test of OBEX 490 */ testBtServer(int type, boolean useSdp, SequencerType sequencerType)491 private void testBtServer(int type, boolean useSdp, SequencerType sequencerType) { 492 mContext = this.getContext(); 493 Log.d(TAG,"Starting BT Server..."); 494 495 if(TRACE) Debug.startMethodTracing("ServerSide"); 496 try { 497 BluetoothServerSocket serverSocket=createServerSocket(type, useSdp); 498 499 Log.i(TAG, "Waiting for client to connect"); 500 BluetoothSocket socket = serverSocket.accept(); 501 Log.i(TAG, "Client connected"); 502 503 BluetoothObexTransport serverTransport = new BluetoothObexTransport(socket); 504 TestSequencer sequencer = null; 505 switch(sequencerType) { 506 case SEQ_TYPE_CONNECT_DISCONNECT: 507 sequencer = createBtConnectTestSequence(null, serverTransport); 508 break; 509 case SEQ_TYPE_PAYLOAD: 510 sequencer = createBtPayloadTestSequence(null, serverTransport); 511 break; 512 default: 513 fail("Invalid sequencer type"); 514 break; 515 516 } 517 //Debug.startMethodTracing("ObexTrace"); 518 assertTrue(sequencer.run(mContext)); 519 //Debug.stopMethodTracing(); 520 // Same as below... serverTransport.close(); 521 // This is done by the obex server socket.close(); 522 serverSocket.close(); 523 removeSdp(); 524 sequencer.shutdown(); 525 } catch (IOException e) { 526 Log.e(TAG, "IOException", e); 527 } 528 if(TRACE) Debug.stopMethodTracing(); 529 if(DELAY_PASS_30_SEC) { 530 Log.i(TAG, "\n\n\nTest done - please fetch logs within 30 seconds...\n\n\n"); 531 try { 532 Thread.sleep(30000); 533 } catch (InterruptedException e) {} 534 } 535 Log.i(TAG, "Test done."); 536 } 537 538 /** 539 * Enable Bluetooth and connect to a server socket 540 * @param type 541 * @param useSdp 542 * @param context 543 * @return 544 * @throws IOException 545 */ connectClientSocket(int type, boolean useSdp, Context context)546 static public BluetoothSocket connectClientSocket(int type, boolean useSdp, Context context) 547 throws IOException { 548 int rfcommChannel = RFCOMM_CHANNEL; 549 int l2capPsm = L2CAP_PSM; 550 551 BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter(); 552 if(bt == null) { 553 Log.e(TAG,"No Bluetooth Device!"); 554 assertTrue(false); 555 } 556 BluetoothTestUtils.enableBt(bt); 557 BluetoothDevice serverDevice = bt.getRemoteDevice(SERVER_ADDRESS); 558 559 if(useSdp == true) { 560 SdpMasRecord record = clientAwaitSdp(serverDevice, context); 561 rfcommChannel = record.getRfcommCannelNumber(); 562 l2capPsm = record.getL2capPsm(); 563 } 564 565 BluetoothSocket socket = null; 566 if(type == BluetoothSocket.TYPE_L2CAP) { 567 socket = serverDevice.createL2capSocket(l2capPsm); 568 } else if(type == BluetoothSocket.TYPE_RFCOMM) { 569 socket = serverDevice.createRfcommSocket(rfcommChannel); 570 } else { 571 fail("Invalid transport type!"); 572 } 573 574 socket.connect(); 575 576 return socket; 577 } 578 579 /** 580 * Test that a connection can be established. 581 */ testBtClient(int type, boolean useSdp, SequencerType sequencerType)582 private void testBtClient(int type, boolean useSdp, SequencerType sequencerType) { 583 mContext = this.getContext(); 584 mChannelType = type; 585 BluetoothSocket socket = null; 586 System.out.println("Starting BT Client..."); 587 if(TRACE) Debug.startMethodTracing("ClientSide"); 588 try { 589 socket = connectClientSocket(type, useSdp, mContext); 590 591 BluetoothObexTransport clientTransport = new BluetoothObexTransport(socket); 592 593 TestSequencer sequencer = null; 594 switch(sequencerType) { 595 case SEQ_TYPE_CONNECT_DISCONNECT: 596 sequencer = createBtConnectTestSequence(clientTransport, null); 597 break; 598 case SEQ_TYPE_PAYLOAD: 599 sequencer = createBtPayloadTestSequence(clientTransport, null); 600 break; 601 default: 602 fail("Invalid test type"); 603 break; 604 605 } 606 //Debug.startMethodTracing("ObexTrace"); 607 assertTrue(sequencer.run(mContext)); 608 //Debug.stopMethodTracing(); 609 socket.close(); // Only the streams are closed by the obex client 610 sequencer.shutdown(); 611 612 } catch (IOException e) { 613 Log.e(TAG, "IOException", e); 614 } 615 if(TRACE) Debug.stopMethodTracing(); 616 if(DELAY_PASS_30_SEC) { 617 Log.i(TAG, "\n\n\nTest done - please fetch logs within 30 seconds...\n\n\n"); 618 try { 619 Thread.sleep(30000); 620 } catch (InterruptedException e) {} 621 } 622 Log.i(TAG, "Test done."); 623 } 624 625 /* Using an anonymous class is not efficient, but keeps a tight code structure. */ 626 static class SdpBroadcastReceiver extends BroadcastReceiver { 627 private SdpMasRecord mMasRecord; /* A non-optimal way of setting an object reference from 628 a anonymous class. */ 629 final CountDownLatch mLatch; SdpBroadcastReceiver(CountDownLatch latch)630 public SdpBroadcastReceiver(CountDownLatch latch) { 631 mLatch = latch; 632 } 633 getMasRecord()634 SdpMasRecord getMasRecord() { 635 return mMasRecord; 636 } 637 638 @Override onReceive(Context context, Intent intent)639 public void onReceive(Context context, Intent intent) { 640 Log.d(TAG, "onReceive"); 641 String action = intent.getAction(); 642 if (action.equals(BluetoothDevice.ACTION_SDP_RECORD)){ 643 Log.v(TAG, "Received ACTION_SDP_RECORD."); 644 ParcelUuid uuid = intent.getParcelableExtra(BluetoothDevice.EXTRA_UUID); 645 Log.v(TAG, "Received UUID: " + uuid.toString()); 646 Log.v(TAG, "existing UUID: " + SDP_UUID_OBEX_MAS.toString()); 647 if(uuid.toString().equals(SDP_UUID_OBEX_MAS.toString())) { 648 assertEquals(SDP_UUID_OBEX_MAS.toString(), uuid.toString()); 649 Log.v(TAG, " -> MAS UUID in result."); 650 SdpMasRecord record = intent.getParcelableExtra( 651 BluetoothDevice.EXTRA_SDP_RECORD); 652 assertNotNull(record); 653 Log.v(TAG, " -> record: "+record); 654 if(record.getServiceName().equals(SDP_SERVER_NAME)) { 655 656 assertEquals(((long)record.getSupportedFeatures()) 657 &0xffffffffL, SDP_FEATURES); 658 659 assertEquals(record.getSupportedMessageTypes(), SDP_MSG_TYPES); 660 661 assertEquals(record.getProfileVersion(), SDP_VERSION); 662 663 assertEquals(record.getServiceName(), SDP_SERVER_NAME); 664 665 assertEquals(record.getMasInstanceId(), SDP_MAS_ID); 666 667 int status = intent.getIntExtra(BluetoothDevice.EXTRA_SDP_SEARCH_STATUS, 668 -1); 669 Log.v(TAG, " -> status: "+status); 670 mMasRecord = record; 671 mLatch.countDown(); 672 } else { 673 Log.i(TAG, "Wrong service name (" + record.getServiceName() 674 + ") received, still waiting..."); 675 } 676 } else { 677 Log.i(TAG, "Wrong UUID received, still waiting..."); 678 } 679 } else { 680 fail("Unexpected intent received???"); 681 } 682 } 683 }; 684 685 clientAwaitSdp(BluetoothDevice serverDevice, Context context)686 private static SdpMasRecord clientAwaitSdp(BluetoothDevice serverDevice, Context context) { 687 IntentFilter filter = new IntentFilter(); 688 filter.addAction(BluetoothDevice.ACTION_SDP_RECORD); 689 final CountDownLatch latch = new CountDownLatch(1); 690 SdpBroadcastReceiver broadcastReceiver = new SdpBroadcastReceiver(latch); 691 692 context.registerReceiver(broadcastReceiver, filter); 693 694 serverDevice.sdpSearch(SDP_UUID_OBEX_MAS); 695 boolean waiting = true; 696 while(waiting == true) { 697 try { 698 Log.i(TAG, "SDP Search requested - awaiting result..."); 699 latch.await(); 700 Log.i(TAG, "SDP Search reresult received - continueing."); 701 waiting = false; 702 } catch (InterruptedException e) { 703 Log.w(TAG, "Interrupted witle waiting - keep waiting.", e); 704 waiting = true; 705 } 706 } 707 context.unregisterReceiver(broadcastReceiver); 708 return broadcastReceiver.getMasRecord(); 709 } 710 711 @Override getObexServer(ArrayList<SeqStep> sequence, CountDownLatch stopLatch)712 public ServerRequestHandler getObexServer(ArrayList<SeqStep> sequence, 713 CountDownLatch stopLatch) { 714 return new ObexTestServer(sequence, stopLatch); 715 } 716 717 718 719 } 720 721