1 /* 2 * Copyright (C) 2014 The Android Open Source Project 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 android.bluetooth.client.map; 18 19 import android.bluetooth.BluetoothDevice; 20 import android.bluetooth.BluetoothMasInstance; 21 import android.bluetooth.BluetoothSocket; 22 import android.os.Handler; 23 import android.os.Message; 24 import android.util.Log; 25 26 import android.bluetooth.client.map.BluetoothMasRequestSetMessageStatus.StatusIndicator; 27 import android.bluetooth.client.map.utils.ObexTime; 28 29 import java.io.IOException; 30 import java.lang.ref.WeakReference; 31 import java.math.BigInteger; 32 import java.util.ArrayDeque; 33 import java.util.ArrayList; 34 import java.util.Date; 35 import java.util.Iterator; 36 37 import javax.obex.ObexTransport; 38 39 public class BluetoothMasClient { 40 41 private final static String TAG = "BluetoothMasClient"; 42 43 private static final int SOCKET_CONNECTED = 10; 44 45 private static final int SOCKET_ERROR = 11; 46 47 /** 48 * Callback message sent when connection state changes 49 * <p> 50 * <code>arg1</code> is set to {@link #STATUS_OK} when connection is 51 * established successfully and {@link #STATUS_FAILED} when connection 52 * either failed or was disconnected (depends on request from application) 53 * 54 * @see #connect() 55 * @see #disconnect() 56 */ 57 public static final int EVENT_CONNECT = 1; 58 59 /** 60 * Callback message sent when MSE accepted update inbox request 61 * 62 * @see #updateInbox() 63 */ 64 public static final int EVENT_UPDATE_INBOX = 2; 65 66 /** 67 * Callback message sent when path is changed 68 * <p> 69 * <code>obj</code> is set to path currently set on MSE 70 * 71 * @see #setFolderRoot() 72 * @see #setFolderUp() 73 * @see #setFolderDown(String) 74 */ 75 public static final int EVENT_SET_PATH = 3; 76 77 /** 78 * Callback message sent when folder listing is received 79 * <p> 80 * <code>obj</code> contains ArrayList of sub-folder names 81 * 82 * @see #getFolderListing() 83 * @see #getFolderListing(int, int) 84 */ 85 public static final int EVENT_GET_FOLDER_LISTING = 4; 86 87 /** 88 * Callback message sent when folder listing size is received 89 * <p> 90 * <code>obj</code> contains number of items in folder listing 91 * 92 * @see #getFolderListingSize() 93 */ 94 public static final int EVENT_GET_FOLDER_LISTING_SIZE = 5; 95 96 /** 97 * Callback message sent when messages listing is received 98 * <p> 99 * <code>obj</code> contains ArrayList of {@link BluetoothMapBmessage} 100 * 101 * @see #getMessagesListing(String, int) 102 * @see #getMessagesListing(String, int, MessagesFilter, int) 103 * @see #getMessagesListing(String, int, MessagesFilter, int, int, int) 104 */ 105 public static final int EVENT_GET_MESSAGES_LISTING = 6; 106 107 /** 108 * Callback message sent when message is received 109 * <p> 110 * <code>obj</code> contains {@link BluetoothMapBmessage} 111 * 112 * @see #getMessage(String, CharsetType, boolean) 113 */ 114 public static final int EVENT_GET_MESSAGE = 7; 115 116 /** 117 * Callback message sent when message status is changed 118 * 119 * @see #setMessageDeletedStatus(String, boolean) 120 * @see #setMessageReadStatus(String, boolean) 121 */ 122 public static final int EVENT_SET_MESSAGE_STATUS = 8; 123 124 /** 125 * Callback message sent when message is pushed to MSE 126 * <p> 127 * <code>obj</code> contains handle of message as allocated by MSE 128 * 129 * @see #pushMessage(String, BluetoothMapBmessage, CharsetType) 130 * @see #pushMessage(String, BluetoothMapBmessage, CharsetType, boolean, 131 * boolean) 132 */ 133 public static final int EVENT_PUSH_MESSAGE = 9; 134 135 /** 136 * Callback message sent when notification status is changed 137 * <p> 138 * <code>obj</code> contains <code>1</code> if notifications are enabled and 139 * <code>0</code> otherwise 140 * 141 * @see #setNotificationRegistration(boolean) 142 */ 143 public static final int EVENT_SET_NOTIFICATION_REGISTRATION = 10; 144 145 /** 146 * Callback message sent when event report is received from MSE to MNS 147 * <p> 148 * <code>obj</code> contains {@link BluetoothMapEventReport} 149 * 150 * @see #setNotificationRegistration(boolean) 151 */ 152 public static final int EVENT_EVENT_REPORT = 11; 153 154 /** 155 * Callback message sent when messages listing size is received 156 * <p> 157 * <code>obj</code> contains number of items in messages listing 158 * 159 * @see #getMessagesListingSize() 160 */ 161 public static final int EVENT_GET_MESSAGES_LISTING_SIZE = 12; 162 163 /** 164 * Status for callback message when request is successful 165 */ 166 public static final int STATUS_OK = 0; 167 168 /** 169 * Status for callback message when request is not successful 170 */ 171 public static final int STATUS_FAILED = 1; 172 173 /** 174 * Constant corresponding to <code>ParameterMask</code> application 175 * parameter value in MAP specification 176 */ 177 public static final int PARAMETER_DEFAULT = 0x00000000; 178 179 /** 180 * Constant corresponding to <code>ParameterMask</code> application 181 * parameter value in MAP specification 182 */ 183 public static final int PARAMETER_SUBJECT = 0x00000001; 184 185 /** 186 * Constant corresponding to <code>ParameterMask</code> application 187 * parameter value in MAP specification 188 */ 189 public static final int PARAMETER_DATETIME = 0x00000002; 190 191 /** 192 * Constant corresponding to <code>ParameterMask</code> application 193 * parameter value in MAP specification 194 */ 195 public static final int PARAMETER_SENDER_NAME = 0x00000004; 196 197 /** 198 * Constant corresponding to <code>ParameterMask</code> application 199 * parameter value in MAP specification 200 */ 201 public static final int PARAMETER_SENDER_ADDRESSING = 0x00000008; 202 203 /** 204 * Constant corresponding to <code>ParameterMask</code> application 205 * parameter value in MAP specification 206 */ 207 public static final int PARAMETER_RECIPIENT_NAME = 0x00000010; 208 209 /** 210 * Constant corresponding to <code>ParameterMask</code> application 211 * parameter value in MAP specification 212 */ 213 public static final int PARAMETER_RECIPIENT_ADDRESSING = 0x00000020; 214 215 /** 216 * Constant corresponding to <code>ParameterMask</code> application 217 * parameter value in MAP specification 218 */ 219 public static final int PARAMETER_TYPE = 0x00000040; 220 221 /** 222 * Constant corresponding to <code>ParameterMask</code> application 223 * parameter value in MAP specification 224 */ 225 public static final int PARAMETER_SIZE = 0x00000080; 226 227 /** 228 * Constant corresponding to <code>ParameterMask</code> application 229 * parameter value in MAP specification 230 */ 231 public static final int PARAMETER_RECEPTION_STATUS = 0x00000100; 232 233 /** 234 * Constant corresponding to <code>ParameterMask</code> application 235 * parameter value in MAP specification 236 */ 237 public static final int PARAMETER_TEXT = 0x00000200; 238 239 /** 240 * Constant corresponding to <code>ParameterMask</code> application 241 * parameter value in MAP specification 242 */ 243 public static final int PARAMETER_ATTACHMENT_SIZE = 0x00000400; 244 245 /** 246 * Constant corresponding to <code>ParameterMask</code> application 247 * parameter value in MAP specification 248 */ 249 public static final int PARAMETER_PRIORITY = 0x00000800; 250 251 /** 252 * Constant corresponding to <code>ParameterMask</code> application 253 * parameter value in MAP specification 254 */ 255 public static final int PARAMETER_READ = 0x00001000; 256 257 /** 258 * Constant corresponding to <code>ParameterMask</code> application 259 * parameter value in MAP specification 260 */ 261 public static final int PARAMETER_SENT = 0x00002000; 262 263 /** 264 * Constant corresponding to <code>ParameterMask</code> application 265 * parameter value in MAP specification 266 */ 267 public static final int PARAMETER_PROTECTED = 0x00004000; 268 269 /** 270 * Constant corresponding to <code>ParameterMask</code> application 271 * parameter value in MAP specification 272 */ 273 public static final int PARAMETER_REPLYTO_ADDRESSING = 0x00008000; 274 275 public enum ConnectionState { 276 DISCONNECTED, CONNECTING, CONNECTED, DISCONNECTING; 277 } 278 279 public enum CharsetType { 280 NATIVE, UTF_8; 281 } 282 283 /** device associated with client */ 284 private final BluetoothDevice mDevice; 285 286 /** MAS instance associated with client */ 287 private final BluetoothMasInstance mMas; 288 289 /** callback handler to application */ 290 private final Handler mCallback; 291 292 private ConnectionState mConnectionState = ConnectionState.DISCONNECTED; 293 294 private boolean mNotificationEnabled = false; 295 296 private SocketConnectThread mConnectThread = null; 297 298 private ObexTransport mObexTransport = null; 299 300 private BluetoothMasObexClientSession mObexSession = null; 301 302 private SessionHandler mSessionHandler = null; 303 304 private BluetoothMnsService mMnsService = null; 305 306 private ArrayDeque<String> mPath = null; 307 308 private static class SessionHandler extends Handler { 309 310 private final WeakReference<BluetoothMasClient> mClient; 311 312 public SessionHandler(BluetoothMasClient client) { 313 super(); 314 315 mClient = new WeakReference<BluetoothMasClient>(client); 316 } 317 318 @Override 319 public void handleMessage(Message msg) { 320 321 BluetoothMasClient client = mClient.get(); 322 if (client == null) { 323 return; 324 } 325 Log.v(TAG, "handleMessage "+msg.what); 326 327 switch (msg.what) { 328 case SOCKET_ERROR: 329 client.mConnectThread = null; 330 client.sendToClient(EVENT_CONNECT, false); 331 break; 332 333 case SOCKET_CONNECTED: 334 client.mConnectThread = null; 335 336 client.mObexTransport = (ObexTransport) msg.obj; 337 338 client.mObexSession = new BluetoothMasObexClientSession(client.mObexTransport, 339 client.mSessionHandler); 340 client.mObexSession.start(); 341 break; 342 343 case BluetoothMasObexClientSession.MSG_OBEX_CONNECTED: 344 client.mPath.clear(); // we're in root after connected 345 client.mConnectionState = ConnectionState.CONNECTED; 346 client.sendToClient(EVENT_CONNECT, true); 347 break; 348 349 case BluetoothMasObexClientSession.MSG_OBEX_DISCONNECTED: 350 client.mConnectionState = ConnectionState.DISCONNECTED; 351 client.mNotificationEnabled = false; 352 client.mObexSession = null; 353 client.sendToClient(EVENT_CONNECT, false); 354 break; 355 356 case BluetoothMasObexClientSession.MSG_REQUEST_COMPLETED: 357 BluetoothMasRequest request = (BluetoothMasRequest) msg.obj; 358 int status = request.isSuccess() ? STATUS_OK : STATUS_FAILED; 359 360 Log.v(TAG, "MSG_REQUEST_COMPLETED (" + status + ") for " 361 + request.getClass().getName()); 362 363 if (request instanceof BluetoothMasRequestUpdateInbox) { 364 client.sendToClient(EVENT_UPDATE_INBOX, request.isSuccess()); 365 366 } else if (request instanceof BluetoothMasRequestSetPath) { 367 if (request.isSuccess()) { 368 BluetoothMasRequestSetPath req = (BluetoothMasRequestSetPath) request; 369 switch (req.mDir) { 370 case UP: 371 if (client.mPath.size() > 0) { 372 client.mPath.removeLast(); 373 } 374 break; 375 376 case ROOT: 377 client.mPath.clear(); 378 break; 379 380 case DOWN: 381 client.mPath.addLast(req.mName); 382 break; 383 } 384 } 385 386 client.sendToClient(EVENT_SET_PATH, request.isSuccess(), 387 client.getCurrentPath()); 388 389 } else if (request instanceof BluetoothMasRequestGetFolderListing) { 390 BluetoothMasRequestGetFolderListing req = (BluetoothMasRequestGetFolderListing) request; 391 ArrayList<String> folders = req.getList(); 392 393 client.sendToClient(EVENT_GET_FOLDER_LISTING, request.isSuccess(), folders); 394 395 } else if (request instanceof BluetoothMasRequestGetFolderListingSize) { 396 int size = ((BluetoothMasRequestGetFolderListingSize) request).getSize(); 397 398 client.sendToClient(EVENT_GET_FOLDER_LISTING_SIZE, request.isSuccess(), 399 size); 400 401 } else if (request instanceof BluetoothMasRequestGetMessagesListing) { 402 BluetoothMasRequestGetMessagesListing req = (BluetoothMasRequestGetMessagesListing) request; 403 ArrayList<BluetoothMapMessage> msgs = req.getList(); 404 405 client.sendToClient(EVENT_GET_MESSAGES_LISTING, request.isSuccess(), msgs); 406 407 } else if (request instanceof BluetoothMasRequestGetMessage) { 408 BluetoothMasRequestGetMessage req = (BluetoothMasRequestGetMessage) request; 409 BluetoothMapBmessage bmsg = req.getMessage(); 410 411 client.sendToClient(EVENT_GET_MESSAGE, request.isSuccess(), bmsg); 412 413 } else if (request instanceof BluetoothMasRequestSetMessageStatus) { 414 client.sendToClient(EVENT_SET_MESSAGE_STATUS, request.isSuccess()); 415 416 } else if (request instanceof BluetoothMasRequestPushMessage) { 417 BluetoothMasRequestPushMessage req = (BluetoothMasRequestPushMessage) request; 418 String handle = req.getMsgHandle(); 419 420 client.sendToClient(EVENT_PUSH_MESSAGE, request.isSuccess(), handle); 421 422 } else if (request instanceof BluetoothMasRequestSetNotificationRegistration) { 423 BluetoothMasRequestSetNotificationRegistration req = (BluetoothMasRequestSetNotificationRegistration) request; 424 425 client.mNotificationEnabled = req.isSuccess() ? req.getStatus() 426 : client.mNotificationEnabled; 427 428 client.sendToClient(EVENT_SET_NOTIFICATION_REGISTRATION, 429 request.isSuccess(), 430 client.mNotificationEnabled ? 1 : 0); 431 } else if (request instanceof BluetoothMasRequestGetMessagesListingSize) { 432 int size = ((BluetoothMasRequestGetMessagesListingSize) request).getSize(); 433 client.sendToClient(EVENT_GET_MESSAGES_LISTING_SIZE, request.isSuccess(), 434 size); 435 } 436 break; 437 438 case BluetoothMnsService.EVENT_REPORT: 439 /* pass event report directly to app */ 440 client.sendToClient(EVENT_EVENT_REPORT, true, msg.obj); 441 break; 442 } 443 } 444 } 445 446 private void sendToClient(int event, boolean success) { 447 sendToClient(event, success, null); 448 } 449 450 private void sendToClient(int event, boolean success, int param) { 451 sendToClient(event, success, Integer.valueOf(param)); 452 } 453 454 private void sendToClient(int event, boolean success, Object param) { 455 if (success) { 456 mCallback.obtainMessage(event, STATUS_OK, mMas.getId(), param).sendToTarget(); 457 } else { 458 mCallback.obtainMessage(event, STATUS_FAILED, mMas.getId(), null).sendToTarget(); 459 } 460 } 461 462 private class SocketConnectThread extends Thread { 463 private BluetoothSocket socket = null; 464 465 public SocketConnectThread() { 466 super("SocketConnectThread"); 467 } 468 469 @Override 470 public void run() { 471 try { 472 socket = mDevice.createRfcommSocket(mMas.getChannel()); 473 socket.connect(); 474 475 BluetoothMapRfcommTransport transport; 476 transport = new BluetoothMapRfcommTransport(socket); 477 478 mSessionHandler.obtainMessage(SOCKET_CONNECTED, transport).sendToTarget(); 479 } catch (IOException e) { 480 Log.e(TAG, "Error when creating/connecting socket", e); 481 482 closeSocket(); 483 mSessionHandler.obtainMessage(SOCKET_ERROR).sendToTarget(); 484 } 485 } 486 487 @Override 488 public void interrupt() { 489 closeSocket(); 490 } 491 492 private void closeSocket() { 493 try { 494 if (socket != null) { 495 socket.close(); 496 } 497 } catch (IOException e) { 498 Log.e(TAG, "Error when closing socket", e); 499 } 500 } 501 } 502 503 /** 504 * Object representation of filters to be applied on message listing 505 * 506 * @see #getMessagesListing(String, int, MessagesFilter, int) 507 * @see #getMessagesListing(String, int, MessagesFilter, int, int, int) 508 */ 509 public static final class MessagesFilter { 510 511 public final static byte MESSAGE_TYPE_ALL = 0x00; 512 public final static byte MESSAGE_TYPE_SMS_GSM = 0x01; 513 public final static byte MESSAGE_TYPE_SMS_CDMA = 0x02; 514 public final static byte MESSAGE_TYPE_EMAIL = 0x04; 515 public final static byte MESSAGE_TYPE_MMS = 0x08; 516 517 public final static byte READ_STATUS_ANY = 0x00; 518 public final static byte READ_STATUS_UNREAD = 0x01; 519 public final static byte READ_STATUS_READ = 0x02; 520 521 public final static byte PRIORITY_ANY = 0x00; 522 public final static byte PRIORITY_HIGH = 0x01; 523 public final static byte PRIORITY_NON_HIGH = 0x02; 524 525 byte messageType = MESSAGE_TYPE_ALL; 526 527 String periodBegin = null; 528 529 String periodEnd = null; 530 531 byte readStatus = READ_STATUS_ANY; 532 533 String recipient = null; 534 535 String originator = null; 536 537 byte priority = PRIORITY_ANY; 538 539 public MessagesFilter() { 540 } 541 542 public void setMessageType(byte filter) { 543 messageType = filter; 544 } 545 546 public void setPeriod(Date filterBegin, Date filterEnd) { 547 periodBegin = (new ObexTime(filterBegin)).toString(); 548 periodEnd = (new ObexTime(filterEnd)).toString(); 549 } 550 551 public void setReadStatus(byte readfilter) { 552 readStatus = readfilter; 553 } 554 555 public void setRecipient(String filter) { 556 if ("".equals(filter)) { 557 recipient = null; 558 } else { 559 recipient = filter; 560 } 561 } 562 563 public void setOriginator(String filter) { 564 if ("".equals(filter)) { 565 originator = null; 566 } else { 567 originator = filter; 568 } 569 } 570 571 public void setPriority(byte filter) { 572 priority = filter; 573 } 574 } 575 576 /** 577 * Constructs client object to communicate with single MAS instance on MSE 578 * 579 * @param device {@link BluetoothDevice} corresponding to remote device 580 * acting as MSE 581 * @param mas {@link BluetoothMasInstance} object describing MAS instance on 582 * remote device 583 * @param callback {@link Handler} object to which callback messages will be 584 * sent Each message will have <code>arg1</code> set to either 585 * {@link #STATUS_OK} or {@link #STATUS_FAILED} and 586 * <code>arg2</code> to MAS instance ID. <code>obj</code> in 587 * message is event specific. 588 */ 589 public BluetoothMasClient(BluetoothDevice device, BluetoothMasInstance mas, 590 Handler callback) { 591 mDevice = device; 592 mMas = mas; 593 mCallback = callback; 594 595 mPath = new ArrayDeque<String>(); 596 } 597 598 /** 599 * Retrieves MAS instance data associated with client 600 * 601 * @return instance data object 602 */ 603 public BluetoothMasInstance getInstanceData() { 604 return mMas; 605 } 606 607 /** 608 * Connects to MAS instance 609 * <p> 610 * Upon completion callback handler will receive {@link #EVENT_CONNECT} 611 */ 612 public void connect() { 613 if (mSessionHandler == null) { 614 mSessionHandler = new SessionHandler(this); 615 } 616 617 if (mConnectThread == null && mObexSession == null) { 618 mConnectionState = ConnectionState.CONNECTING; 619 620 mConnectThread = new SocketConnectThread(); 621 mConnectThread.start(); 622 } 623 } 624 625 /** 626 * Disconnects from MAS instance 627 * <p> 628 * Upon completion callback handler will receive {@link #EVENT_CONNECT} 629 */ 630 public void disconnect() { 631 if (mConnectThread == null && mObexSession == null) { 632 return; 633 } 634 635 mConnectionState = ConnectionState.DISCONNECTING; 636 637 if (mConnectThread != null) { 638 mConnectThread.interrupt(); 639 } 640 641 if (mObexSession != null) { 642 mObexSession.stop(); 643 } 644 } 645 646 @Override 647 public void finalize() { 648 disconnect(); 649 } 650 651 /** 652 * Gets current connection state 653 * 654 * @return current connection state 655 * @see ConnectionState 656 */ 657 public ConnectionState getState() { 658 return mConnectionState; 659 } 660 661 private boolean enableNotifications() { 662 Log.v(TAG, "enableNotifications()"); 663 664 if (mMnsService == null) { 665 mMnsService = new BluetoothMnsService(); 666 } 667 668 mMnsService.registerCallback(mMas.getId(), mSessionHandler); 669 670 BluetoothMasRequest request = new BluetoothMasRequestSetNotificationRegistration(true); 671 return mObexSession.makeRequest(request); 672 } 673 674 private boolean disableNotifications() { 675 Log.v(TAG, "enableNotifications()"); 676 677 if (mMnsService != null) { 678 mMnsService.unregisterCallback(mMas.getId()); 679 } 680 681 mMnsService = null; 682 683 BluetoothMasRequest request = new BluetoothMasRequestSetNotificationRegistration(false); 684 return mObexSession.makeRequest(request); 685 } 686 687 /** 688 * Sets state of notifications for MAS instance 689 * <p> 690 * Once notifications are enabled, callback handler will receive 691 * {@link #EVENT_EVENT_REPORT} when new notification is received 692 * <p> 693 * Upon completion callback handler will receive 694 * {@link #EVENT_SET_NOTIFICATION_REGISTRATION} 695 * 696 * @param status <code>true</code> if notifications shall be enabled, 697 * <code>false</code> otherwise 698 * @return <code>true</code> if request has been sent, <code>false</code> 699 * otherwise 700 */ 701 public boolean setNotificationRegistration(boolean status) { 702 if (mObexSession == null) { 703 return false; 704 } 705 706 if (status) { 707 return enableNotifications(); 708 } else { 709 return disableNotifications(); 710 } 711 } 712 713 /** 714 * Gets current state of notifications for MAS instance 715 * 716 * @return <code>true</code> if notifications are enabled, 717 * <code>false</code> otherwise 718 */ 719 public boolean getNotificationRegistration() { 720 return mNotificationEnabled; 721 } 722 723 /** 724 * Goes back to root of folder hierarchy 725 * <p> 726 * Upon completion callback handler will receive {@link #EVENT_SET_PATH} 727 * 728 * @return <code>true</code> if request has been sent, <code>false</code> 729 * otherwise 730 */ 731 public boolean setFolderRoot() { 732 if (mObexSession == null) { 733 return false; 734 } 735 736 BluetoothMasRequest request = new BluetoothMasRequestSetPath(true); 737 return mObexSession.makeRequest(request); 738 } 739 740 /** 741 * Goes back to parent folder in folder hierarchy 742 * <p> 743 * Upon completion callback handler will receive {@link #EVENT_SET_PATH} 744 * 745 * @return <code>true</code> if request has been sent, <code>false</code> 746 * otherwise 747 */ 748 public boolean setFolderUp() { 749 if (mObexSession == null) { 750 return false; 751 } 752 753 BluetoothMasRequest request = new BluetoothMasRequestSetPath(false); 754 return mObexSession.makeRequest(request); 755 } 756 757 /** 758 * Goes down to specified sub-folder in folder hierarchy 759 * <p> 760 * Upon completion callback handler will receive {@link #EVENT_SET_PATH} 761 * 762 * @param name name of sub-folder 763 * @return <code>true</code> if request has been sent, <code>false</code> 764 * otherwise 765 */ 766 public boolean setFolderDown(String name) { 767 if (mObexSession == null) { 768 return false; 769 } 770 771 if (name == null || name.isEmpty() || name.contains("/")) { 772 return false; 773 } 774 775 BluetoothMasRequest request = new BluetoothMasRequestSetPath(name); 776 return mObexSession.makeRequest(request); 777 } 778 779 /** 780 * Gets current path in folder hierarchy 781 * 782 * @return current path 783 */ 784 public String getCurrentPath() { 785 if (mPath.size() == 0) { 786 return ""; 787 } 788 789 Iterator<String> iter = mPath.iterator(); 790 791 StringBuilder sb = new StringBuilder(iter.next()); 792 793 while (iter.hasNext()) { 794 sb.append("/").append(iter.next()); 795 } 796 797 return sb.toString(); 798 } 799 800 /** 801 * Gets list of sub-folders in current folder 802 * <p> 803 * Upon completion callback handler will receive 804 * {@link #EVENT_GET_FOLDER_LISTING} 805 * 806 * @return <code>true</code> if request has been sent, <code>false</code> 807 * otherwise 808 */ 809 public boolean getFolderListing() { 810 return getFolderListing((short) 0, (short) 0); 811 } 812 813 /** 814 * Gets list of sub-folders in current folder 815 * <p> 816 * Upon completion callback handler will receive 817 * {@link #EVENT_GET_FOLDER_LISTING} 818 * 819 * @param maxListCount maximum number of items returned or <code>0</code> 820 * for default value 821 * @param listStartOffset index of first item returned or <code>0</code> for 822 * default value 823 * @return <code>true</code> if request has been sent, <code>false</code> 824 * otherwise 825 * @throws IllegalArgumentException if either maxListCount or 826 * listStartOffset are outside allowed range [0..65535] 827 */ 828 public boolean getFolderListing(int maxListCount, int listStartOffset) { 829 if (mObexSession == null) { 830 return false; 831 } 832 833 BluetoothMasRequest request = new BluetoothMasRequestGetFolderListing(maxListCount, 834 listStartOffset); 835 return mObexSession.makeRequest(request); 836 } 837 838 /** 839 * Gets number of sub-folders in current folder 840 * <p> 841 * Upon completion callback handler will receive 842 * {@link #EVENT_GET_FOLDER_LISTING_SIZE} 843 * 844 * @return <code>true</code> if request has been sent, <code>false</code> 845 * otherwise 846 */ 847 public boolean getFolderListingSize() { 848 if (mObexSession == null) { 849 return false; 850 } 851 852 BluetoothMasRequest request = new BluetoothMasRequestGetFolderListingSize(); 853 return mObexSession.makeRequest(request); 854 } 855 856 /** 857 * Gets list of messages in specified sub-folder 858 * <p> 859 * Upon completion callback handler will receive 860 * {@link #EVENT_GET_MESSAGES_LISTING} 861 * 862 * @param folder name of sub-folder or <code>null</code> for current folder 863 * @param parameters bit-mask specifying requested parameters in listing or 864 * <code>0</code> for default value 865 * @return <code>true</code> if request has been sent, <code>false</code> 866 * otherwise 867 */ 868 public boolean getMessagesListing(String folder, int parameters) { 869 return getMessagesListing(folder, parameters, null, (byte) 0, 0, 0); 870 } 871 872 /** 873 * Gets list of messages in specified sub-folder 874 * <p> 875 * Upon completion callback handler will receive 876 * {@link #EVENT_GET_MESSAGES_LISTING} 877 * 878 * @param folder name of sub-folder or <code>null</code> for current folder 879 * @param parameters corresponds to <code>ParameterMask</code> application 880 * parameter in MAP specification 881 * @param filter {@link MessagesFilter} object describing filters to be 882 * applied on listing by MSE 883 * @param subjectLength maximum length of message subject in returned 884 * listing or <code>0</code> for default value 885 * @return <code>true</code> if request has been sent, <code>false</code> 886 * otherwise 887 * @throws IllegalArgumentException if subjectLength is outside allowed 888 * range [0..255] 889 */ 890 public boolean getMessagesListing(String folder, int parameters, MessagesFilter filter, 891 int subjectLength) { 892 893 return getMessagesListing(folder, parameters, filter, subjectLength, 0, 0); 894 } 895 896 /** 897 * Gets list of messages in specified sub-folder 898 * <p> 899 * Upon completion callback handler will receive 900 * {@link #EVENT_GET_MESSAGES_LISTING} 901 * 902 * @param folder name of sub-folder or <code>null</code> for current folder 903 * @param parameters corresponds to <code>ParameterMask</code> application 904 * parameter in MAP specification 905 * @param filter {@link MessagesFilter} object describing filters to be 906 * applied on listing by MSE 907 * @param subjectLength maximum length of message subject in returned 908 * listing or <code>0</code> for default value 909 * @param maxListCount maximum number of items returned or <code>0</code> 910 * for default value 911 * @param listStartOffset index of first item returned or <code>0</code> for 912 * default value 913 * @return <code>true</code> if request has been sent, <code>false</code> 914 * otherwise 915 * @throws IllegalArgumentException if subjectLength is outside allowed 916 * range [0..255] or either maxListCount or listStartOffset are 917 * outside allowed range [0..65535] 918 */ 919 public boolean getMessagesListing(String folder, int parameters, MessagesFilter filter, 920 int subjectLength, int maxListCount, int listStartOffset) { 921 922 if (mObexSession == null) { 923 return false; 924 } 925 926 BluetoothMasRequest request = new BluetoothMasRequestGetMessagesListing(folder, 927 parameters, filter, subjectLength, maxListCount, listStartOffset); 928 return mObexSession.makeRequest(request); 929 } 930 931 /** 932 * Gets number of messages in current folder 933 * <p> 934 * Upon completion callback handler will receive 935 * {@link #EVENT_GET_MESSAGES_LISTING_SIZE} 936 * 937 * @return <code>true</code> if request has been sent, <code>false</code> 938 * otherwise 939 */ 940 public boolean getMessagesListingSize() { 941 if (mObexSession == null) { 942 return false; 943 } 944 945 BluetoothMasRequest request = new BluetoothMasRequestGetMessagesListingSize(); 946 return mObexSession.makeRequest(request); 947 } 948 949 /** 950 * Retrieves message from MSE 951 * <p> 952 * Upon completion callback handler will receive {@link #EVENT_GET_MESSAGE} 953 * 954 * @param handle handle of message to retrieve 955 * @param charset {@link CharsetType} object corresponding to 956 * <code>Charset</code> application parameter in MAP 957 * specification 958 * @param attachment corresponds to <code>Attachment</code> application 959 * parameter in MAP specification 960 * @return <code>true</code> if request has been sent, <code>false</code> 961 * otherwise 962 */ 963 public boolean getMessage(String handle, CharsetType charset, boolean attachment) { 964 if (mObexSession == null) { 965 return false; 966 } 967 968 try { 969 /* just to validate */ 970 new BigInteger(handle, 16); 971 } catch (NumberFormatException e) { 972 return false; 973 } 974 975 BluetoothMasRequest request = new BluetoothMasRequestGetMessage(handle, charset, 976 attachment); 977 return mObexSession.makeRequest(request); 978 } 979 980 /** 981 * Sets read status of message on MSE 982 * <p> 983 * Upon completion callback handler will receive 984 * {@link #EVENT_SET_MESSAGE_STATUS} 985 * 986 * @param handle handle of message 987 * @param read <code>true</code> for "read", <code>false</code> for "unread" 988 * @return <code>true</code> if request has been sent, <code>false</code> 989 * otherwise 990 */ 991 public boolean setMessageReadStatus(String handle, boolean read) { 992 if (mObexSession == null) { 993 return false; 994 } 995 996 try { 997 /* just to validate */ 998 new BigInteger(handle, 16); 999 } catch (NumberFormatException e) { 1000 return false; 1001 } 1002 1003 BluetoothMasRequest request = new BluetoothMasRequestSetMessageStatus(handle, 1004 StatusIndicator.READ, read); 1005 return mObexSession.makeRequest(request); 1006 } 1007 1008 /** 1009 * Sets deleted status of message on MSE 1010 * <p> 1011 * Upon completion callback handler will receive 1012 * {@link #EVENT_SET_MESSAGE_STATUS} 1013 * 1014 * @param handle handle of message 1015 * @param deleted <code>true</code> for "deleted", <code>false</code> for 1016 * "undeleted" 1017 * @return <code>true</code> if request has been sent, <code>false</code> 1018 * otherwise 1019 */ 1020 public boolean setMessageDeletedStatus(String handle, boolean deleted) { 1021 if (mObexSession == null) { 1022 return false; 1023 } 1024 1025 try { 1026 /* just to validate */ 1027 new BigInteger(handle, 16); 1028 } catch (NumberFormatException e) { 1029 return false; 1030 } 1031 1032 BluetoothMasRequest request = new BluetoothMasRequestSetMessageStatus(handle, 1033 StatusIndicator.DELETED, deleted); 1034 return mObexSession.makeRequest(request); 1035 } 1036 1037 /** 1038 * Pushes new message to MSE 1039 * <p> 1040 * Upon completion callback handler will receive {@link #EVENT_PUSH_MESSAGE} 1041 * 1042 * @param folder name of sub-folder to push to or <code>null</code> for 1043 * current folder 1044 * @param charset {@link CharsetType} object corresponding to 1045 * <code>Charset</code> application parameter in MAP 1046 * specification 1047 * @return <code>true</code> if request has been sent, <code>false</code> 1048 * otherwise 1049 */ 1050 public boolean pushMessage(String folder, BluetoothMapBmessage bmsg, CharsetType charset) { 1051 return pushMessage(folder, bmsg, charset, false, false); 1052 } 1053 1054 /** 1055 * Pushes new message to MSE 1056 * <p> 1057 * Upon completion callback handler will receive {@link #EVENT_PUSH_MESSAGE} 1058 * 1059 * @param folder name of sub-folder to push to or <code>null</code> for 1060 * current folder 1061 * @param bmsg {@link BluetoothMapBmessage} object representing message to 1062 * be pushed 1063 * @param charset {@link CharsetType} object corresponding to 1064 * <code>Charset</code> application parameter in MAP 1065 * specification 1066 * @param transparent corresponds to <code>Transparent</code> application 1067 * parameter in MAP specification 1068 * @param retry corresponds to <code>Transparent</code> application 1069 * parameter in MAP specification 1070 * @return <code>true</code> if request has been sent, <code>false</code> 1071 * otherwise 1072 */ 1073 public boolean pushMessage(String folder, BluetoothMapBmessage bmsg, CharsetType charset, 1074 boolean transparent, boolean retry) { 1075 if (mObexSession == null) { 1076 return false; 1077 } 1078 1079 String bmsgString = BluetoothMapBmessageBuilder.createBmessage(bmsg); 1080 1081 BluetoothMasRequest request = 1082 new BluetoothMasRequestPushMessage(folder, bmsgString, charset, transparent, retry); 1083 return mObexSession.makeRequest(request); 1084 } 1085 1086 /** 1087 * Requests MSE to initiate ubdate of inbox 1088 * <p> 1089 * Upon completion callback handler will receive {@link #EVENT_UPDATE_INBOX} 1090 * 1091 * @return <code>true</code> if request has been sent, <code>false</code> 1092 * otherwise 1093 */ 1094 public boolean updateInbox() { 1095 if (mObexSession == null) { 1096 return false; 1097 } 1098 1099 BluetoothMasRequest request = new BluetoothMasRequestUpdateInbox(); 1100 return mObexSession.makeRequest(request); 1101 } 1102 } 1103