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