• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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