1package com.android.bluetooth.tests;
2
3import java.io.IOException;
4import java.util.ArrayList;
5import java.util.concurrent.CountDownLatch;
6
7import javax.obex.ClientSession;
8import javax.obex.HeaderSet;
9import javax.obex.ObexTransport;
10import javax.obex.Operation;
11import javax.obex.ServerSession;
12
13import junit.framework.Assert;
14
15import android.content.Context;
16import android.hardware.camera2.impl.GetCommand;
17import android.os.Handler;
18import android.os.Handler.Callback;
19import android.os.HandlerThread;
20import android.os.Message;
21import android.os.PowerManager;
22import android.util.Log;
23
24public class TestSequencer implements Callback {
25    protected static String TAG = "TestSequencer";
26    protected static final boolean D = true;
27
28    private final static int MSG_ID_TIMEOUT = 0x01;
29    private final static int TIMEOUT_VALUE = 100*2000; // ms
30    private ArrayList<SeqStep> mSequence = null;
31    private HandlerThread mHandlerThread = null;
32    private Handler mMessageHandler = null;
33    private ObexTransport mClientTransport;
34    private ObexTransport mServerTransport;
35
36    private ClientSession mClientSession = null;
37    private ServerSession mServerSession = null;
38    public static final int STEP_INDEX_HEADER = 0xF1; /*0xFE*/
39
40    public enum OPTYPE {CONNECT, PUT, GET, SET_PATH, DISCONNECT};
41
42    private ITestSequenceConfigurator mConfigurator = null;
43
44    public TestSequencer(ObexTransport clientTransport, ObexTransport serverTransport,
45            ITestSequenceConfigurator configurator)
46            throws IOException {
47        /* Setup the looper thread to handle timeout messages */
48//            mHandlerThread = new HandlerThread("TestTimeoutHandler",
49//                      android.os.Process.THREAD_PRIORITY_BACKGROUND);
50//            mHandlerThread.start();
51//            Looper testLooper = mHandlerThread.getLooper();
52//            mMessageHandler = new Handler(testLooper, this);
53        //TODO: fix looper cleanup on server - crash after 464 iterations - related to prepare?
54
55        mClientTransport = clientTransport;
56        mServerTransport = serverTransport;
57
58        /* Initialize members */
59        mSequence = new ArrayList<SeqStep>();
60        mConfigurator = configurator;
61        Assert.assertNotNull(configurator);
62    }
63
64    /**
65     * Add a test step to the sequencer.
66     * @param type the OBEX operation to perform.
67     * @return the created step, which can be decorated before execution.
68     */
69    public SeqStep addStep(OPTYPE type, ISeqStepValidator validator) {
70        SeqStep newStep = new SeqStep(type);
71        newStep.mValidator = validator;
72        mSequence.add(newStep);
73        return newStep;
74    }
75
76    /**
77     * Add a sub-step to a sequencer step. All requests added to the same index will be send to
78     * the SapServer in the order added before listening for the response.
79     * The response order is not validated - hence for each response received the entire list of
80     * responses in the step will be searched for a match.
81     * @param index the index returned from addStep() to which the sub-step is to be added.
82     * @param request The request to send to the SAP server
83     * @param response The response to EXPECT from the SAP server
84
85    public void addSubStep(int index, SapMessage request, SapMessage response) {
86        SeqStep step = sequence.get(index);
87        step.add(request, response);
88    }*/
89
90
91    /**
92     * Run the sequence.
93     * Validate the response is either the expected response or one of the expected events.
94     *
95     * @return true when done - asserts at error/fail
96     */
97    public boolean run(Context context) throws IOException {
98        CountDownLatch stopLatch = new CountDownLatch(1);
99        PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
100        PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
101        //wl.acquire();
102        try {
103            /* TODO:
104             * First create sequencer to validate using BT-snoop
105             * 1) Create the transports (this could include a validation sniffer on each side)
106             * 2) Create a server thread with a link to the transport
107             * 3) execute the client operation
108             * 4) validate response
109             *
110             * On server:
111             * 1) validate the request contains the expected content
112             * 2) send response.
113             * */
114
115            /* Create the server */
116            if(mServerTransport != null) {
117                mServerSession = new ServerSession(mServerTransport,
118                        mConfigurator.getObexServer(mSequence, stopLatch) , null);
119            }
120
121            /* Create the client */
122            if(mClientTransport != null) {
123                mClientSession = new ClientSession(mClientTransport);
124
125                for(SeqStep step : mSequence) {
126                    long stepIndex = mSequence.indexOf(step);
127
128                    Log.i(TAG, "Executing step " + stepIndex + " of type: " + step.mType);
129
130                    switch(step.mType) {
131                    case CONNECT: {
132                        HeaderSet reqHeaders = step.mReqHeaders;
133                        if(reqHeaders == null) {
134                            reqHeaders = new HeaderSet();
135                        }
136                        reqHeaders.setHeader(STEP_INDEX_HEADER, stepIndex);
137                        HeaderSet response = mClientSession.connect(reqHeaders);
138                        step.validate(response, null);
139                        step.clientPostAction(response, null);
140                        break;
141                    }
142                    case GET:{
143                        HeaderSet reqHeaders = step.mReqHeaders;
144                        if(reqHeaders == null) {
145                            reqHeaders = new HeaderSet();
146                        }
147                        reqHeaders.setHeader(STEP_INDEX_HEADER, stepIndex);
148                        Log.i(TAG, "  Starting operation...");
149                        Operation op = mClientSession.get(reqHeaders);
150                        Log.i(TAG, "  Operation done...");
151                        step.validate(null, op);
152                        step.clientPostAction(null, op);
153                        break;
154                    }
155                    case PUT: {
156                        HeaderSet reqHeaders = step.mReqHeaders;
157                        if(reqHeaders == null) {
158                            reqHeaders = new HeaderSet();
159                        }
160                        reqHeaders.setHeader(STEP_INDEX_HEADER, stepIndex);
161                        Operation op = mClientSession.put(reqHeaders);
162                        step.validate(null, op);
163                        step.clientPostAction(null, op);
164                        break;
165                    }
166                    case SET_PATH: {
167                        HeaderSet reqHeaders = step.mReqHeaders;
168                        if(reqHeaders == null) {
169                            reqHeaders = new HeaderSet();
170                        }
171                        reqHeaders.setHeader(STEP_INDEX_HEADER, stepIndex);
172                        try{
173                            HeaderSet response = mClientSession.setPath(reqHeaders,
174                                    step.mSetPathBackup, step.mSetPathCreate);;
175                            Log.i(TAG,"Received setPath response...");
176                            step.validate(response, null);
177                            step.clientPostAction(response, null);
178                        } catch (IOException e) {
179                            Log.e(TAG, "Error getting response code", e);
180                        }
181                        break;
182                    }
183                    case DISCONNECT: {
184                        Log.i(TAG,"Requesting disconnect...");
185                        HeaderSet reqHeaders = step.mReqHeaders;
186                        if(reqHeaders == null) {
187                            reqHeaders = new HeaderSet();
188                        }
189                        reqHeaders.setHeader(STEP_INDEX_HEADER, stepIndex);
190                        try{
191                            HeaderSet response = mClientSession.disconnect(reqHeaders);
192                            Log.i(TAG,"Received disconnect response...");
193                            step.validate(response, null);
194                            step.clientPostAction(response, null);
195                        } catch (IOException e) {
196                            Log.e(TAG, "Error getting response code", e);
197                        }
198                        break;
199                    }
200                    default:
201                        Assert.assertTrue("Unknown type: " + step.mType, false);
202                        break;
203
204                    }
205                }
206                mClientSession.close();
207            }
208            /* All done, close down... */
209            if(mServerSession != null) {
210                boolean interrupted = false;
211                do {
212                    try {
213                        interrupted = false;
214                        Log.i(TAG,"Waiting for stopLatch signal...");
215                        stopLatch.await();
216                    } catch (InterruptedException e) {
217                        Log.w(TAG,e);
218                        interrupted = true;
219                    }
220                } while (interrupted == true);
221                Log.i(TAG,"stopLatch signal received closing down...");
222                try {
223                    interrupted = false;
224                    Log.i(TAG,"  Sleep 50ms to allow disconnect signal to be send before closing.");
225                    Thread.sleep(50);
226                } catch (InterruptedException e) {
227                    Log.w(TAG,e);
228                    interrupted = true;
229                }
230                mServerSession.close();
231            }
232            // this will close the I/O streams as well.
233        } finally {
234            //wl.release();
235        }
236        return true;
237    }
238
239    public void shutdown() {
240//            mMessageHandler.removeCallbacksAndMessages(null);
241//            mMessageHandler.quit();
242//            mMessageHandler = null;
243    }
244
245
246//        private void startTimer() {
247//            Message timeoutMessage = mMessageHandler.obtainMessage(MSG_ID_TIMEOUT);
248//            mMessageHandler.sendMessageDelayed(timeoutMessage, TIMEOUT_VALUE);
249//        }
250//
251//        private void stopTimer() {
252//            mMessageHandler.removeMessages(MSG_ID_TIMEOUT);
253//        }
254
255    @Override
256    public boolean handleMessage(Message msg) {
257
258        Log.i(TAG,"Handling message ID: " + msg.what);
259
260        switch(msg.what) {
261        case MSG_ID_TIMEOUT:
262            Log.w(TAG, "Timeout occured!");
263/*                try {
264                    //inStream.close();
265                } catch (IOException e) {
266                    Log.e(TAG, "failed to close inStream", e);
267                }
268                try {
269                    //outStream.close();
270                } catch (IOException e) {
271                    Log.e(TAG, "failed to close outStream", e);
272                }*/
273            break;
274        default:
275            /* Message not handled */
276            return false;
277        }
278        return true; // Message handles
279    }
280
281
282
283}
284
285