1/*
2 * Copyright (c) 2008-2009, Motorola, Inc.
3 *
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * - Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 *
12 * - Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
15 *
16 * - Neither the name of the Motorola, Inc. nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
24 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
32
33package com.android.bluetooth.opp;
34
35import com.android.bluetooth.R;
36
37import android.app.Activity;
38import android.content.ContentValues;
39import android.content.Context;
40import android.content.Intent;
41import android.database.Cursor;
42import android.net.Uri;
43import android.os.Bundle;
44import android.os.Handler;
45import android.os.Message;
46import android.util.Log;
47import android.widget.Button;
48import android.widget.EditText;
49import android.view.View;
50import android.view.View.OnClickListener;
51
52import java.io.DataInputStream;
53import java.io.DataOutputStream;
54import java.io.File;
55import java.io.FileOutputStream;
56import java.io.IOException;
57import java.io.InputStream;
58import java.io.OutputStream;
59import java.net.ServerSocket;
60import java.net.Socket;
61import java.net.SocketException;
62
63import javax.obex.Authenticator;
64import javax.obex.HeaderSet;
65import javax.obex.ObexTransport;
66import javax.obex.Operation;
67import javax.obex.ResponseCodes;
68import javax.obex.ServerRequestHandler;
69import javax.obex.ServerSession;
70
71public class TestActivity extends Activity {
72
73    public String currentInsert;
74
75    public int mCurrentByte = 0;
76
77    EditText mUpdateView;
78
79    EditText mAckView;
80
81    EditText mDeleteView;
82
83    EditText mInsertView;
84
85    EditText mAddressView;
86
87    EditText mMediaView;
88
89    TestTcpServer server;
90
91    /** Called when the activity is first created. */
92    @Override
93    public void onCreate(Bundle savedInstanceState) {
94        super.onCreate(savedInstanceState);
95
96        Intent intent = getIntent();
97
98        String action = intent.getAction();
99
100        Context c = getBaseContext();
101
102        if (Intent.ACTION_SEND.equals(action)) {
103            /*
104             * Other application is trying to share a file via Bluetooth,
105             * probably Pictures, or vCard. The Intent should contain an
106             * EXTRA_STREAM with the data to attach.
107             */
108
109            String type = intent.getType();
110            Uri stream = (Uri)intent.getParcelableExtra(Intent.EXTRA_STREAM);
111
112            if (stream != null && type != null) {
113                /*
114                 * if (MimeUtility.mimeTypeMatches(type,
115                 * Email.ACCEPTABLE_ATTACHMENT_SEND_TYPES)) {
116                 * addAttachment(stream);
117                 */
118                Log.v(Constants.TAG, " Get share intent with Uri " + stream + " mimetype is "
119                        + type);
120                // Log.v(Constants.TAG, " trying Uri function " +
121                // stream.getAuthority() + " " + Uri.parse(stream));
122                Cursor cursor = c.getContentResolver().query(stream, null, null, null, null);
123                cursor.close();
124
125            }
126            /* start insert a record */
127            /*
128             * ContentValues values = new ContentValues();
129             * values.put(BluetoothShare.URI, stream.toString());
130             * values.put(BluetoothShare.DESTINATION, "FF:FF:FF:00:00:00");
131             * values.put(BluetoothShare.DIRECTION,
132             * BluetoothShare.DIRECTION_OUTBOUND); final Uri contentUri =
133             * getContentResolver().insert(BluetoothShare.CONTENT_URI, values);
134             * Log.v(Constants.TAG, "insert contentUri: " + contentUri);
135             */
136        }
137        /*
138         * Context c = getBaseContext(); c.startService(new Intent(c,
139         * BluetoothOppService.class));
140         */
141
142        setContentView(R.layout.testactivity_main);
143
144        Button mInsertRecord = (Button)findViewById(R.id.insert_record);
145        Button mDeleteRecord = (Button)findViewById(R.id.delete_record);
146        Button mUpdateRecord = (Button)findViewById(R.id.update_record);
147
148        Button mAckRecord = (Button)findViewById(R.id.ack_record);
149
150        Button mDeleteAllRecord = (Button)findViewById(R.id.deleteAll_record);
151        mUpdateView = (EditText)findViewById(R.id.update_text);
152        mAckView = (EditText)findViewById(R.id.ack_text);
153        mDeleteView = (EditText)findViewById(R.id.delete_text);
154        mInsertView = (EditText)findViewById(R.id.insert_text);
155
156        mAddressView = (EditText)findViewById(R.id.address_text);
157        mMediaView = (EditText)findViewById(R.id.media_text);
158
159        mInsertRecord.setOnClickListener(insertRecordListener);
160        mDeleteRecord.setOnClickListener(deleteRecordListener);
161        mUpdateRecord.setOnClickListener(updateRecordListener);
162        mAckRecord.setOnClickListener(ackRecordListener);
163        mDeleteAllRecord.setOnClickListener(deleteAllRecordListener);
164
165        Button mStartTcpServer = (Button)findViewById(R.id.start_server);
166        mStartTcpServer.setOnClickListener(startTcpServerListener);
167
168        Button mNotifyTcpServer = (Button)findViewById(R.id.notify_server);
169        mNotifyTcpServer.setOnClickListener(notifyTcpServerListener);
170        /* parse insert result Uri */
171        /*
172         * String id = contentUri.getPathSegments().get(1); Log.v(Constants.TAG,
173         * "insert record id is " + id); Uri contentUri1 =
174         * Uri.parse(BluetoothShare.CONTENT_URI + "/" + id);
175         */
176        /* update a single column of a record */
177        /*
178         * ContentValues updateValues = new ContentValues();
179         * updateValues.put(BluetoothShare.TOTAL_BYTES, 120000);
180         * getContentResolver().update(contentUri1,updateValues,null,null);
181         */
182        /* query a single column of a record */
183        /*
184         * Cursor queryC = getContentResolver().query(contentUri1, null, null,
185         * null, null); if (queryC != null) { if (queryC.moveToFirst()) { int
186         * currentByteColumn =
187         * queryC.getColumnIndexOrThrow(BluetoothShare.CURRENT_BYTES); int
188         * currentByte = queryC.getInt(currentByteColumn);
189         */
190        /* update a column of a record */
191        /*
192         * for(int i =0;i<100;i++){ currentByte ++;
193         * updateValues.put(BluetoothShare.CURRENT_BYTES, currentByte);
194         * getContentResolver().update(contentUri1,updateValues,null,null); } }
195         * }
196         */
197        /* query whole data base */
198        /*
199         * Cursor c = managedQuery(contentUri1, new String [] {"_id",
200         * BluetoothShare.URI, BluetoothShare.STATUS,
201         * BluetoothShare.TOTAL_BYTES, BluetoothShare.CURRENT_BYTES,
202         * BluetoothShare._DATA, BluetoothShare.DIRECTION,
203         * BluetoothShare.MIMETYPE, BluetoothShare.DESTINATION,
204         * BluetoothShare.VISIBILITY, BluetoothShare.USER_CONFIRMATION,
205         * BluetoothShare.TIMESTAMP}, null, null, null); Log.v(Constants.TAG,
206         * "query " + contentUri1 +" get " + c.getCount()+" records");
207         */
208        /* delete a record */
209        /*
210         * Uri contentUri2 = Uri.parse(BluetoothShare.CONTENT_URI + "/" + 1);
211         * getContentResolver().delete(contentUri2, null, null);
212         */
213
214    }
215
216    public OnClickListener insertRecordListener = new OnClickListener() {
217        public void onClick(View view) {
218
219            String address = null;
220            if (mAddressView.getText().length() != 0) {
221                address = mAddressView.getText().toString();
222                Log.v(Constants.TAG, "Send to address  " + address);
223            }
224            if (address == null) {
225                address = "00:17:83:58:5D:CC";
226            }
227
228            Integer media = null;
229            if (mMediaView.getText().length() != 0) {
230                media = Integer.parseInt(mMediaView.getText().toString().trim());
231                Log.v(Constants.TAG, "Send media no.  " + media);
232            }
233            if (media == null) {
234                media = 1;
235            }
236            ContentValues values = new ContentValues();
237            values.put(BluetoothShare.URI, "content://media/external/images/media/" + media);
238            // values.put(BluetoothShare.DESTINATION, "FF:FF:FF:00:00:00");
239            // baibai Q9 test
240            // values.put(BluetoothShare.DESTINATION, "12:34:56:78:9A:BC");
241            // java's nokia
242            // values.put(BluetoothShare.DESTINATION, "00:1B:33:F0:58:FB");
243            // Assis phone
244            // values.put(BluetoothShare.DESTINATION, "00:17:E5:5D:74:F3");
245            // Jackson E6
246            // values.put(BluetoothShare.DESTINATION, "00:1A:1B:7F:1E:F0");
247            // Baibai V950
248            // values.put(BluetoothShare.DESTINATION, "00:17:83:58:5D:CC");
249            // Baibai NSC1173
250            // values.put(BluetoothShare.DESTINATION, "00:16:41:49:5B:F3");
251
252            values.put(BluetoothShare.DESTINATION, address);
253
254            values.put(BluetoothShare.DIRECTION, BluetoothShare.DIRECTION_OUTBOUND);
255
256            Long ts = System.currentTimeMillis();
257            values.put(BluetoothShare.TIMESTAMP, ts);
258
259            Integer records = null;
260            if (mInsertView.getText().length() != 0) {
261                records = Integer.parseInt(mInsertView.getText().toString().trim());
262                Log.v(Constants.TAG, "parseInt  " + records);
263            }
264            if (records == null) {
265                records = 1;
266            }
267            for (int i = 0; i < records; i++) {
268                Uri contentUri = getContentResolver().insert(BluetoothShare.CONTENT_URI, values);
269                Log.v(Constants.TAG, "insert contentUri: " + contentUri);
270                currentInsert = contentUri.getPathSegments().get(1);
271                Log.v(Constants.TAG, "currentInsert = " + currentInsert);
272            }
273
274        }
275    };
276
277    public OnClickListener deleteRecordListener = new OnClickListener() {
278        public void onClick(View view) {
279            Uri contentUri = Uri.parse(BluetoothShare.CONTENT_URI + "/"
280                    + mDeleteView.getText().toString());
281            getContentResolver().delete(contentUri, null, null);
282        }
283    };
284
285    public OnClickListener updateRecordListener = new OnClickListener() {
286        public void onClick(View view) {
287            Uri contentUri = Uri.parse(BluetoothShare.CONTENT_URI + "/"
288                    + mUpdateView.getText().toString());
289            ContentValues updateValues = new ContentValues();
290            // mCurrentByte ++;
291            // updateValues.put(BluetoothShare.TOTAL_BYTES, "120000");
292            // updateValues.put(BluetoothShare.CURRENT_BYTES, mCurrentByte);
293            // updateValues.put(BluetoothShare.VISIBILITY,
294            // BluetoothShare.VISIBILITY_HIDDEN);
295            updateValues.put(BluetoothShare.USER_CONFIRMATION,
296                    BluetoothShare.USER_CONFIRMATION_CONFIRMED);
297            getContentResolver().update(contentUri, updateValues, null, null);
298        }
299    };
300
301    public OnClickListener ackRecordListener = new OnClickListener() {
302        public void onClick(View view) {
303            Uri contentUri = Uri.parse(BluetoothShare.CONTENT_URI + "/"
304                    + mAckView.getText().toString());
305            ContentValues updateValues = new ContentValues();
306            // mCurrentByte ++;
307            // updateValues.put(BluetoothShare.TOTAL_BYTES, "120000");
308            // updateValues.put(BluetoothShare.CURRENT_BYTES, mCurrentByte);
309            updateValues.put(BluetoothShare.VISIBILITY, BluetoothShare.VISIBILITY_HIDDEN);
310            // updateValues.put(BluetoothShare.USER_CONFIRMATION,
311            // BluetoothShare.USER_CONFIRMATION_CONFIRMED);
312            getContentResolver().update(contentUri, updateValues, null, null);
313        }
314    };
315
316    public OnClickListener deleteAllRecordListener = new OnClickListener() {
317        public void onClick(View view) {
318            Uri contentUri = Uri.parse(BluetoothShare.CONTENT_URI + "");
319            getContentResolver().delete(contentUri, null, null);
320        }
321    };
322
323    public OnClickListener startTcpServerListener = new OnClickListener() {
324        public void onClick(View view) {
325            server = new TestTcpServer();
326            Thread server_thread = new Thread(server);
327            server_thread.start();
328
329        }
330    };
331
332    public OnClickListener notifyTcpServerListener = new OnClickListener() {
333        public void onClick(View view) {
334            final Thread notifyThread = new Thread() {
335                public void run() {
336                    synchronized (server) {
337                        server.a = true;
338                        server.notify();
339                    }
340                }
341
342            };
343            notifyThread.start();
344        }
345
346    };
347}
348
349/**
350 * This class listens on OPUSH channel for incoming connection
351 */
352class TestTcpListener {
353
354    private static final String TAG = "BtOppRfcommListener";
355
356    private static final boolean D = Constants.DEBUG;
357
358    private static final boolean V = Constants.VERBOSE;
359
360    private volatile boolean mInterrupted;
361
362    private Thread mSocketAcceptThread;
363
364    private Handler mCallback;
365
366    private static final int ACCEPT_WAIT_TIMEOUT = 5000;
367
368    public static final int DEFAULT_OPP_CHANNEL = 12;
369
370    public static final int MSG_INCOMING_BTOPP_CONNECTION = 100;
371
372    private int mBtOppRfcommChannel = -1;
373
374    public TestTcpListener() {
375        this(DEFAULT_OPP_CHANNEL);
376    }
377
378    public TestTcpListener(int channel) {
379        mBtOppRfcommChannel = channel;
380    }
381
382    public synchronized boolean start(Handler callback) {
383        if (mSocketAcceptThread == null) {
384            mCallback = callback;
385            mSocketAcceptThread = new Thread(TAG) {
386                ServerSocket mServerSocket;
387
388                public void run() {
389                    if (D) Log.d(TAG, "RfcommSocket listen thread starting");
390                    try {
391                        if (V)
392                            Log.v(TAG, "Create server RfcommSocket on channel"
393                                    + mBtOppRfcommChannel);
394                        mServerSocket = new ServerSocket(6500, 1);
395                    } catch (IOException e) {
396                        Log.e(TAG, "Error listing on channel" + mBtOppRfcommChannel);
397                        mInterrupted = true;
398                    }
399                    while (!mInterrupted) {
400                        try {
401                            mServerSocket.setSoTimeout(ACCEPT_WAIT_TIMEOUT);
402                            Socket clientSocket = mServerSocket.accept();
403                            if (clientSocket == null) {
404                                if (V) Log.v(TAG, "incomming connection time out");
405                            } else {
406                                if (D) Log.d(TAG, "RfcommSocket connected!");
407                                Log.d(TAG, "remote addr is "
408                                        + clientSocket.getRemoteSocketAddress());
409                                TestTcpTransport transport = new TestTcpTransport(clientSocket);
410                                Message msg = Message.obtain();
411                                msg.setTarget(mCallback);
412                                msg.what = MSG_INCOMING_BTOPP_CONNECTION;
413                                msg.obj = transport;
414                                msg.sendToTarget();
415                            }
416                        } catch (SocketException e) {
417                            Log.e(TAG, "Error accept connection " + e);
418                        } catch (IOException e) {
419                            Log.e(TAG, "Error accept connection " + e);
420                        }
421
422                        if (mInterrupted) {
423                            Log.e(TAG, "socketAcceptThread thread was interrupted (2), exiting");
424                        }
425                    }
426                    if (D) Log.d(TAG, "RfcommSocket listen thread finished");
427                }
428            };
429            mInterrupted = false;
430            mSocketAcceptThread.start();
431
432        }
433        return true;
434
435    }
436
437    public synchronized void stop() {
438        if (mSocketAcceptThread != null) {
439            if (D) Log.d(TAG, "stopping Connect Thread");
440            mInterrupted = true;
441            try {
442                mSocketAcceptThread.interrupt();
443                if (V) Log.v(TAG, "waiting for thread to terminate");
444                mSocketAcceptThread.join();
445                mSocketAcceptThread = null;
446                mCallback = null;
447            } catch (InterruptedException e) {
448                if (V) Log.v(TAG, "Interrupted waiting for Accept Thread to join");
449            }
450        }
451    }
452
453}
454
455class TestTcpServer extends ServerRequestHandler implements Runnable {
456    private static final String TAG = "ServerRequestHandler";
457
458    private static final boolean V = Constants.VERBOSE;
459
460    static final int port = 6500;
461
462    public boolean a = false;
463
464    // TextView serverStatus = null;
465    public void run() {
466        try {
467            updateStatus("[server:] listen on port " + port);
468            TestTcpSessionNotifier rsn = new TestTcpSessionNotifier(port);
469
470            updateStatus("[server:] Now waiting for a client to connect");
471            rsn.acceptAndOpen(this);
472            updateStatus("[server:] A client is now connected");
473        } catch (Exception ex) {
474            updateStatus("[server:] Caught the error: " + ex);
475        }
476    }
477
478    public TestTcpServer() {
479        updateStatus("enter construtor of TcpServer");
480    }
481
482    public int onConnect(HeaderSet request, HeaderSet reply) {
483
484        updateStatus("[server:] The client has created an OBEX session");
485        /* sleep for 2000 ms to wait for the batch contains all ShareInfos */
486        synchronized (this) {
487            try {
488                while (!a) {
489                    wait(500);
490                }
491            } catch (InterruptedException e) {
492                if (V) Log.v(TAG, "Interrupted waiting for markBatchFailed");
493            }
494        }
495        updateStatus("[server:] we accpet the seesion");
496        return ResponseCodes.OBEX_HTTP_OK;
497    }
498
499    public int onPut(Operation op) {
500        FileOutputStream fos = null;
501        try {
502            java.io.InputStream is = op.openInputStream();
503
504            updateStatus("Got data bytes " + is.available() + " name "
505                    + op.getReceivedHeader().getHeader(HeaderSet.NAME) + " type " + op.getType());
506
507            File f = new File((String)op.getReceivedHeader().getHeader(HeaderSet.NAME));
508            fos = new FileOutputStream(f);
509            byte b[] = new byte[1000];
510            int len;
511
512            while (is.available() > 0 && (len = is.read(b)) > 0) {
513                fos.write(b, 0, len);
514            }
515
516            fos.close();
517            is.close();
518            updateStatus("[server:] Wrote data to " + f.getAbsolutePath());
519        } catch (Exception e) {
520            if (fos != null) {
521                try {
522                    fos.close();
523                } catch (IOException e1) {
524                    e1.printStackTrace();
525                }
526            }
527            e.printStackTrace();
528        }
529        return ResponseCodes.OBEX_HTTP_OK;
530    }
531
532    public void onDisconnect(HeaderSet req, HeaderSet resp) {
533        updateStatus("[server:] The client has disconnected the OBEX session");
534    }
535
536    public void updateStatus(String message) {
537        Log.v(TAG, "\n" + message);
538    }
539
540    public void onAuthenticationFailure(byte[] userName) {
541    }
542
543    public int onSetPath(HeaderSet request, HeaderSet reply, boolean backup, boolean create) {
544
545        return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
546    }
547
548    public int onDelete(HeaderSet request, HeaderSet reply) {
549        return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
550    }
551
552    public int onGet(Operation op) {
553        return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
554    }
555
556}
557
558class TestTcpSessionNotifier {
559    /* implements SessionNotifier */
560
561    ServerSocket server = null;
562
563    Socket conn = null;
564
565    private static final String TAG = "TestTcpSessionNotifier";
566
567    public TestTcpSessionNotifier(int port) throws IOException {
568        server = new ServerSocket(port);
569    }
570
571    public ServerSession acceptAndOpen(ServerRequestHandler handler, Authenticator auth)
572            throws IOException {
573        try {
574            conn = server.accept();
575
576        } catch (Exception ex) {
577            Log.v(TAG, "ex");
578        }
579
580        TestTcpTransport tt = new TestTcpTransport(conn);
581
582        return new ServerSession((ObexTransport)tt, handler, auth);
583
584    }
585
586    public ServerSession acceptAndOpen(ServerRequestHandler handler) throws IOException {
587
588        return acceptAndOpen(handler, null);
589
590    }
591
592}
593
594class TestTcpTransport implements ObexTransport {
595
596    Socket s = null;
597
598    public TestTcpTransport(Socket s) {
599        super();
600        this.s = s;
601    }
602
603    public void close() throws IOException {
604        s.close();
605    }
606
607    public DataInputStream openDataInputStream() throws IOException {
608        return new DataInputStream(openInputStream());
609    }
610
611    public DataOutputStream openDataOutputStream() throws IOException {
612        return new DataOutputStream(openOutputStream());
613    }
614
615    public InputStream openInputStream() throws IOException {
616        return s.getInputStream();
617    }
618
619    public OutputStream openOutputStream() throws IOException {
620        return s.getOutputStream();
621    }
622
623    public void connect() throws IOException {
624        // TODO Auto-generated method stub
625
626    }
627
628    public void create() throws IOException {
629        // TODO Auto-generated method stub
630
631    }
632
633    public void disconnect() throws IOException {
634        // TODO Auto-generated method stub
635
636    }
637
638    public void listen() throws IOException {
639        // TODO Auto-generated method stub
640
641    }
642
643    public boolean isConnected() throws IOException {
644        return s.isConnected();
645    }
646
647    @Override
648    public int getMaxTransmitPacketSize() {
649        return -1;
650    }
651
652    @Override
653    public int getMaxReceivePacketSize() {
654        return -1;
655    }
656
657    @Override
658    public boolean isSrmSupported() {
659        // TODO: It should be possible to use SRM in TCP connections
660        return false;
661    }
662}
663