summaryrefslogtreecommitdiffstats
path: root/src/android/bluetooth/client/map/BluetoothMasClient.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/android/bluetooth/client/map/BluetoothMasClient.java')
-rw-r--r--src/android/bluetooth/client/map/BluetoothMasClient.java1102
1 files changed, 1102 insertions, 0 deletions
diff --git a/src/android/bluetooth/client/map/BluetoothMasClient.java b/src/android/bluetooth/client/map/BluetoothMasClient.java
new file mode 100644
index 0000000..7d50e5b
--- /dev/null
+++ b/src/android/bluetooth/client/map/BluetoothMasClient.java
@@ -0,0 +1,1102 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth.client.map;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothMasInstance;
+import android.bluetooth.BluetoothSocket;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+
+import android.bluetooth.client.map.BluetoothMasRequestSetMessageStatus.StatusIndicator;
+import android.bluetooth.client.map.utils.ObexTime;
+
+import java.io.IOException;
+import java.lang.ref.WeakReference;
+import java.math.BigInteger;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Iterator;
+
+import javax.obex.ObexTransport;
+
+public class BluetoothMasClient {
+
+ private final static String TAG = "BluetoothMasClient";
+
+ private static final int SOCKET_CONNECTED = 10;
+
+ private static final int SOCKET_ERROR = 11;
+
+ /**
+ * Callback message sent when connection state changes
+ * <p>
+ * <code>arg1</code> is set to {@link #STATUS_OK} when connection is
+ * established successfully and {@link #STATUS_FAILED} when connection
+ * either failed or was disconnected (depends on request from application)
+ *
+ * @see #connect()
+ * @see #disconnect()
+ */
+ public static final int EVENT_CONNECT = 1;
+
+ /**
+ * Callback message sent when MSE accepted update inbox request
+ *
+ * @see #updateInbox()
+ */
+ public static final int EVENT_UPDATE_INBOX = 2;
+
+ /**
+ * Callback message sent when path is changed
+ * <p>
+ * <code>obj</code> is set to path currently set on MSE
+ *
+ * @see #setFolderRoot()
+ * @see #setFolderUp()
+ * @see #setFolderDown(String)
+ */
+ public static final int EVENT_SET_PATH = 3;
+
+ /**
+ * Callback message sent when folder listing is received
+ * <p>
+ * <code>obj</code> contains ArrayList of sub-folder names
+ *
+ * @see #getFolderListing()
+ * @see #getFolderListing(int, int)
+ */
+ public static final int EVENT_GET_FOLDER_LISTING = 4;
+
+ /**
+ * Callback message sent when folder listing size is received
+ * <p>
+ * <code>obj</code> contains number of items in folder listing
+ *
+ * @see #getFolderListingSize()
+ */
+ public static final int EVENT_GET_FOLDER_LISTING_SIZE = 5;
+
+ /**
+ * Callback message sent when messages listing is received
+ * <p>
+ * <code>obj</code> contains ArrayList of {@link BluetoothMapBmessage}
+ *
+ * @see #getMessagesListing(String, int)
+ * @see #getMessagesListing(String, int, MessagesFilter, int)
+ * @see #getMessagesListing(String, int, MessagesFilter, int, int, int)
+ */
+ public static final int EVENT_GET_MESSAGES_LISTING = 6;
+
+ /**
+ * Callback message sent when message is received
+ * <p>
+ * <code>obj</code> contains {@link BluetoothMapBmessage}
+ *
+ * @see #getMessage(String, CharsetType, boolean)
+ */
+ public static final int EVENT_GET_MESSAGE = 7;
+
+ /**
+ * Callback message sent when message status is changed
+ *
+ * @see #setMessageDeletedStatus(String, boolean)
+ * @see #setMessageReadStatus(String, boolean)
+ */
+ public static final int EVENT_SET_MESSAGE_STATUS = 8;
+
+ /**
+ * Callback message sent when message is pushed to MSE
+ * <p>
+ * <code>obj</code> contains handle of message as allocated by MSE
+ *
+ * @see #pushMessage(String, BluetoothMapBmessage, CharsetType)
+ * @see #pushMessage(String, BluetoothMapBmessage, CharsetType, boolean,
+ * boolean)
+ */
+ public static final int EVENT_PUSH_MESSAGE = 9;
+
+ /**
+ * Callback message sent when notification status is changed
+ * <p>
+ * <code>obj</code> contains <code>1</code> if notifications are enabled and
+ * <code>0</code> otherwise
+ *
+ * @see #setNotificationRegistration(boolean)
+ */
+ public static final int EVENT_SET_NOTIFICATION_REGISTRATION = 10;
+
+ /**
+ * Callback message sent when event report is received from MSE to MNS
+ * <p>
+ * <code>obj</code> contains {@link BluetoothMapEventReport}
+ *
+ * @see #setNotificationRegistration(boolean)
+ */
+ public static final int EVENT_EVENT_REPORT = 11;
+
+ /**
+ * Callback message sent when messages listing size is received
+ * <p>
+ * <code>obj</code> contains number of items in messages listing
+ *
+ * @see #getMessagesListingSize()
+ */
+ public static final int EVENT_GET_MESSAGES_LISTING_SIZE = 12;
+
+ /**
+ * Status for callback message when request is successful
+ */
+ public static final int STATUS_OK = 0;
+
+ /**
+ * Status for callback message when request is not successful
+ */
+ public static final int STATUS_FAILED = 1;
+
+ /**
+ * Constant corresponding to <code>ParameterMask</code> application
+ * parameter value in MAP specification
+ */
+ public static final int PARAMETER_DEFAULT = 0x00000000;
+
+ /**
+ * Constant corresponding to <code>ParameterMask</code> application
+ * parameter value in MAP specification
+ */
+ public static final int PARAMETER_SUBJECT = 0x00000001;
+
+ /**
+ * Constant corresponding to <code>ParameterMask</code> application
+ * parameter value in MAP specification
+ */
+ public static final int PARAMETER_DATETIME = 0x00000002;
+
+ /**
+ * Constant corresponding to <code>ParameterMask</code> application
+ * parameter value in MAP specification
+ */
+ public static final int PARAMETER_SENDER_NAME = 0x00000004;
+
+ /**
+ * Constant corresponding to <code>ParameterMask</code> application
+ * parameter value in MAP specification
+ */
+ public static final int PARAMETER_SENDER_ADDRESSING = 0x00000008;
+
+ /**
+ * Constant corresponding to <code>ParameterMask</code> application
+ * parameter value in MAP specification
+ */
+ public static final int PARAMETER_RECIPIENT_NAME = 0x00000010;
+
+ /**
+ * Constant corresponding to <code>ParameterMask</code> application
+ * parameter value in MAP specification
+ */
+ public static final int PARAMETER_RECIPIENT_ADDRESSING = 0x00000020;
+
+ /**
+ * Constant corresponding to <code>ParameterMask</code> application
+ * parameter value in MAP specification
+ */
+ public static final int PARAMETER_TYPE = 0x00000040;
+
+ /**
+ * Constant corresponding to <code>ParameterMask</code> application
+ * parameter value in MAP specification
+ */
+ public static final int PARAMETER_SIZE = 0x00000080;
+
+ /**
+ * Constant corresponding to <code>ParameterMask</code> application
+ * parameter value in MAP specification
+ */
+ public static final int PARAMETER_RECEPTION_STATUS = 0x00000100;
+
+ /**
+ * Constant corresponding to <code>ParameterMask</code> application
+ * parameter value in MAP specification
+ */
+ public static final int PARAMETER_TEXT = 0x00000200;
+
+ /**
+ * Constant corresponding to <code>ParameterMask</code> application
+ * parameter value in MAP specification
+ */
+ public static final int PARAMETER_ATTACHMENT_SIZE = 0x00000400;
+
+ /**
+ * Constant corresponding to <code>ParameterMask</code> application
+ * parameter value in MAP specification
+ */
+ public static final int PARAMETER_PRIORITY = 0x00000800;
+
+ /**
+ * Constant corresponding to <code>ParameterMask</code> application
+ * parameter value in MAP specification
+ */
+ public static final int PARAMETER_READ = 0x00001000;
+
+ /**
+ * Constant corresponding to <code>ParameterMask</code> application
+ * parameter value in MAP specification
+ */
+ public static final int PARAMETER_SENT = 0x00002000;
+
+ /**
+ * Constant corresponding to <code>ParameterMask</code> application
+ * parameter value in MAP specification
+ */
+ public static final int PARAMETER_PROTECTED = 0x00004000;
+
+ /**
+ * Constant corresponding to <code>ParameterMask</code> application
+ * parameter value in MAP specification
+ */
+ public static final int PARAMETER_REPLYTO_ADDRESSING = 0x00008000;
+
+ public enum ConnectionState {
+ DISCONNECTED, CONNECTING, CONNECTED, DISCONNECTING;
+ }
+
+ public enum CharsetType {
+ NATIVE, UTF_8;
+ }
+
+ /** device associated with client */
+ private final BluetoothDevice mDevice;
+
+ /** MAS instance associated with client */
+ private final BluetoothMasInstance mMas;
+
+ /** callback handler to application */
+ private final Handler mCallback;
+
+ private ConnectionState mConnectionState = ConnectionState.DISCONNECTED;
+
+ private boolean mNotificationEnabled = false;
+
+ private SocketConnectThread mConnectThread = null;
+
+ private ObexTransport mObexTransport = null;
+
+ private BluetoothMasObexClientSession mObexSession = null;
+
+ private SessionHandler mSessionHandler = null;
+
+ private BluetoothMnsService mMnsService = null;
+
+ private ArrayDeque<String> mPath = null;
+
+ private static class SessionHandler extends Handler {
+
+ private final WeakReference<BluetoothMasClient> mClient;
+
+ public SessionHandler(BluetoothMasClient client) {
+ super();
+
+ mClient = new WeakReference<BluetoothMasClient>(client);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+
+ BluetoothMasClient client = mClient.get();
+ if (client == null) {
+ return;
+ }
+ Log.v(TAG, "handleMessage "+msg.what);
+
+ switch (msg.what) {
+ case SOCKET_ERROR:
+ client.mConnectThread = null;
+ client.sendToClient(EVENT_CONNECT, false);
+ break;
+
+ case SOCKET_CONNECTED:
+ client.mConnectThread = null;
+
+ client.mObexTransport = (ObexTransport) msg.obj;
+
+ client.mObexSession = new BluetoothMasObexClientSession(client.mObexTransport,
+ client.mSessionHandler);
+ client.mObexSession.start();
+ break;
+
+ case BluetoothMasObexClientSession.MSG_OBEX_CONNECTED:
+ client.mPath.clear(); // we're in root after connected
+ client.mConnectionState = ConnectionState.CONNECTED;
+ client.sendToClient(EVENT_CONNECT, true);
+ break;
+
+ case BluetoothMasObexClientSession.MSG_OBEX_DISCONNECTED:
+ client.mConnectionState = ConnectionState.DISCONNECTED;
+ client.mNotificationEnabled = false;
+ client.mObexSession = null;
+ client.sendToClient(EVENT_CONNECT, false);
+ break;
+
+ case BluetoothMasObexClientSession.MSG_REQUEST_COMPLETED:
+ BluetoothMasRequest request = (BluetoothMasRequest) msg.obj;
+ int status = request.isSuccess() ? STATUS_OK : STATUS_FAILED;
+
+ Log.v(TAG, "MSG_REQUEST_COMPLETED (" + status + ") for "
+ + request.getClass().getName());
+
+ if (request instanceof BluetoothMasRequestUpdateInbox) {
+ client.sendToClient(EVENT_UPDATE_INBOX, request.isSuccess());
+
+ } else if (request instanceof BluetoothMasRequestSetPath) {
+ if (request.isSuccess()) {
+ BluetoothMasRequestSetPath req = (BluetoothMasRequestSetPath) request;
+ switch (req.mDir) {
+ case UP:
+ if (client.mPath.size() > 0) {
+ client.mPath.removeLast();
+ }
+ break;
+
+ case ROOT:
+ client.mPath.clear();
+ break;
+
+ case DOWN:
+ client.mPath.addLast(req.mName);
+ break;
+ }
+ }
+
+ client.sendToClient(EVENT_SET_PATH, request.isSuccess(),
+ client.getCurrentPath());
+
+ } else if (request instanceof BluetoothMasRequestGetFolderListing) {
+ BluetoothMasRequestGetFolderListing req = (BluetoothMasRequestGetFolderListing) request;
+ ArrayList<String> folders = req.getList();
+
+ client.sendToClient(EVENT_GET_FOLDER_LISTING, request.isSuccess(), folders);
+
+ } else if (request instanceof BluetoothMasRequestGetFolderListingSize) {
+ int size = ((BluetoothMasRequestGetFolderListingSize) request).getSize();
+
+ client.sendToClient(EVENT_GET_FOLDER_LISTING_SIZE, request.isSuccess(),
+ size);
+
+ } else if (request instanceof BluetoothMasRequestGetMessagesListing) {
+ BluetoothMasRequestGetMessagesListing req = (BluetoothMasRequestGetMessagesListing) request;
+ ArrayList<BluetoothMapMessage> msgs = req.getList();
+
+ client.sendToClient(EVENT_GET_MESSAGES_LISTING, request.isSuccess(), msgs);
+
+ } else if (request instanceof BluetoothMasRequestGetMessage) {
+ BluetoothMasRequestGetMessage req = (BluetoothMasRequestGetMessage) request;
+ BluetoothMapBmessage bmsg = req.getMessage();
+
+ client.sendToClient(EVENT_GET_MESSAGE, request.isSuccess(), bmsg);
+
+ } else if (request instanceof BluetoothMasRequestSetMessageStatus) {
+ client.sendToClient(EVENT_SET_MESSAGE_STATUS, request.isSuccess());
+
+ } else if (request instanceof BluetoothMasRequestPushMessage) {
+ BluetoothMasRequestPushMessage req = (BluetoothMasRequestPushMessage) request;
+ String handle = req.getMsgHandle();
+
+ client.sendToClient(EVENT_PUSH_MESSAGE, request.isSuccess(), handle);
+
+ } else if (request instanceof BluetoothMasRequestSetNotificationRegistration) {
+ BluetoothMasRequestSetNotificationRegistration req = (BluetoothMasRequestSetNotificationRegistration) request;
+
+ client.mNotificationEnabled = req.isSuccess() ? req.getStatus()
+ : client.mNotificationEnabled;
+
+ client.sendToClient(EVENT_SET_NOTIFICATION_REGISTRATION,
+ request.isSuccess(),
+ client.mNotificationEnabled ? 1 : 0);
+ } else if (request instanceof BluetoothMasRequestGetMessagesListingSize) {
+ int size = ((BluetoothMasRequestGetMessagesListingSize) request).getSize();
+ client.sendToClient(EVENT_GET_MESSAGES_LISTING_SIZE, request.isSuccess(),
+ size);
+ }
+ break;
+
+ case BluetoothMnsService.EVENT_REPORT:
+ /* pass event report directly to app */
+ client.sendToClient(EVENT_EVENT_REPORT, true, msg.obj);
+ break;
+ }
+ }
+ }
+
+ private void sendToClient(int event, boolean success) {
+ sendToClient(event, success, null);
+ }
+
+ private void sendToClient(int event, boolean success, int param) {
+ sendToClient(event, success, Integer.valueOf(param));
+ }
+
+ private void sendToClient(int event, boolean success, Object param) {
+ if (success) {
+ mCallback.obtainMessage(event, STATUS_OK, mMas.getId(), param).sendToTarget();
+ } else {
+ mCallback.obtainMessage(event, STATUS_FAILED, mMas.getId(), null).sendToTarget();
+ }
+ }
+
+ private class SocketConnectThread extends Thread {
+ private BluetoothSocket socket = null;
+
+ public SocketConnectThread() {
+ super("SocketConnectThread");
+ }
+
+ @Override
+ public void run() {
+ try {
+ socket = mDevice.createRfcommSocket(mMas.getChannel());
+ socket.connect();
+
+ BluetoothMapRfcommTransport transport;
+ transport = new BluetoothMapRfcommTransport(socket);
+
+ mSessionHandler.obtainMessage(SOCKET_CONNECTED, transport).sendToTarget();
+ } catch (IOException e) {
+ Log.e(TAG, "Error when creating/connecting socket", e);
+
+ closeSocket();
+ mSessionHandler.obtainMessage(SOCKET_ERROR).sendToTarget();
+ }
+ }
+
+ @Override
+ public void interrupt() {
+ closeSocket();
+ }
+
+ private void closeSocket() {
+ try {
+ if (socket != null) {
+ socket.close();
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "Error when closing socket", e);
+ }
+ }
+ }
+
+ /**
+ * Object representation of filters to be applied on message listing
+ *
+ * @see #getMessagesListing(String, int, MessagesFilter, int)
+ * @see #getMessagesListing(String, int, MessagesFilter, int, int, int)
+ */
+ public static final class MessagesFilter {
+
+ public final static byte MESSAGE_TYPE_ALL = 0x00;
+ public final static byte MESSAGE_TYPE_SMS_GSM = 0x01;
+ public final static byte MESSAGE_TYPE_SMS_CDMA = 0x02;
+ public final static byte MESSAGE_TYPE_EMAIL = 0x04;
+ public final static byte MESSAGE_TYPE_MMS = 0x08;
+
+ public final static byte READ_STATUS_ANY = 0x00;
+ public final static byte READ_STATUS_UNREAD = 0x01;
+ public final static byte READ_STATUS_READ = 0x02;
+
+ public final static byte PRIORITY_ANY = 0x00;
+ public final static byte PRIORITY_HIGH = 0x01;
+ public final static byte PRIORITY_NON_HIGH = 0x02;
+
+ byte messageType = MESSAGE_TYPE_ALL;
+
+ String periodBegin = null;
+
+ String periodEnd = null;
+
+ byte readStatus = READ_STATUS_ANY;
+
+ String recipient = null;
+
+ String originator = null;
+
+ byte priority = PRIORITY_ANY;
+
+ public MessagesFilter() {
+ }
+
+ public void setMessageType(byte filter) {
+ messageType = filter;
+ }
+
+ public void setPeriod(Date filterBegin, Date filterEnd) {
+ periodBegin = (new ObexTime(filterBegin)).toString();
+ periodEnd = (new ObexTime(filterEnd)).toString();
+ }
+
+ public void setReadStatus(byte readfilter) {
+ readStatus = readfilter;
+ }
+
+ public void setRecipient(String filter) {
+ if ("".equals(filter)) {
+ recipient = null;
+ } else {
+ recipient = filter;
+ }
+ }
+
+ public void setOriginator(String filter) {
+ if ("".equals(filter)) {
+ originator = null;
+ } else {
+ originator = filter;
+ }
+ }
+
+ public void setPriority(byte filter) {
+ priority = filter;
+ }
+ }
+
+ /**
+ * Constructs client object to communicate with single MAS instance on MSE
+ *
+ * @param device {@link BluetoothDevice} corresponding to remote device
+ * acting as MSE
+ * @param mas {@link BluetoothMasInstance} object describing MAS instance on
+ * remote device
+ * @param callback {@link Handler} object to which callback messages will be
+ * sent Each message will have <code>arg1</code> set to either
+ * {@link #STATUS_OK} or {@link #STATUS_FAILED} and
+ * <code>arg2</code> to MAS instance ID. <code>obj</code> in
+ * message is event specific.
+ */
+ public BluetoothMasClient(BluetoothDevice device, BluetoothMasInstance mas,
+ Handler callback) {
+ mDevice = device;
+ mMas = mas;
+ mCallback = callback;
+
+ mPath = new ArrayDeque<String>();
+ }
+
+ /**
+ * Retrieves MAS instance data associated with client
+ *
+ * @return instance data object
+ */
+ public BluetoothMasInstance getInstanceData() {
+ return mMas;
+ }
+
+ /**
+ * Connects to MAS instance
+ * <p>
+ * Upon completion callback handler will receive {@link #EVENT_CONNECT}
+ */
+ public void connect() {
+ if (mSessionHandler == null) {
+ mSessionHandler = new SessionHandler(this);
+ }
+
+ if (mConnectThread == null && mObexSession == null) {
+ mConnectionState = ConnectionState.CONNECTING;
+
+ mConnectThread = new SocketConnectThread();
+ mConnectThread.start();
+ }
+ }
+
+ /**
+ * Disconnects from MAS instance
+ * <p>
+ * Upon completion callback handler will receive {@link #EVENT_CONNECT}
+ */
+ public void disconnect() {
+ if (mConnectThread == null && mObexSession == null) {
+ return;
+ }
+
+ mConnectionState = ConnectionState.DISCONNECTING;
+
+ if (mConnectThread != null) {
+ mConnectThread.interrupt();
+ }
+
+ if (mObexSession != null) {
+ mObexSession.stop();
+ }
+ }
+
+ @Override
+ public void finalize() {
+ disconnect();
+ }
+
+ /**
+ * Gets current connection state
+ *
+ * @return current connection state
+ * @see ConnectionState
+ */
+ public ConnectionState getState() {
+ return mConnectionState;
+ }
+
+ private boolean enableNotifications() {
+ Log.v(TAG, "enableNotifications()");
+
+ if (mMnsService == null) {
+ mMnsService = new BluetoothMnsService();
+ }
+
+ mMnsService.registerCallback(mMas.getId(), mSessionHandler);
+
+ BluetoothMasRequest request = new BluetoothMasRequestSetNotificationRegistration(true);
+ return mObexSession.makeRequest(request);
+ }
+
+ private boolean disableNotifications() {
+ Log.v(TAG, "enableNotifications()");
+
+ if (mMnsService != null) {
+ mMnsService.unregisterCallback(mMas.getId());
+ }
+
+ mMnsService = null;
+
+ BluetoothMasRequest request = new BluetoothMasRequestSetNotificationRegistration(false);
+ return mObexSession.makeRequest(request);
+ }
+
+ /**
+ * Sets state of notifications for MAS instance
+ * <p>
+ * Once notifications are enabled, callback handler will receive
+ * {@link #EVENT_EVENT_REPORT} when new notification is received
+ * <p>
+ * Upon completion callback handler will receive
+ * {@link #EVENT_SET_NOTIFICATION_REGISTRATION}
+ *
+ * @param status <code>true</code> if notifications shall be enabled,
+ * <code>false</code> otherwise
+ * @return <code>true</code> if request has been sent, <code>false</code>
+ * otherwise
+ */
+ public boolean setNotificationRegistration(boolean status) {
+ if (mObexSession == null) {
+ return false;
+ }
+
+ if (status) {
+ return enableNotifications();
+ } else {
+ return disableNotifications();
+ }
+ }
+
+ /**
+ * Gets current state of notifications for MAS instance
+ *
+ * @return <code>true</code> if notifications are enabled,
+ * <code>false</code> otherwise
+ */
+ public boolean getNotificationRegistration() {
+ return mNotificationEnabled;
+ }
+
+ /**
+ * Goes back to root of folder hierarchy
+ * <p>
+ * Upon completion callback handler will receive {@link #EVENT_SET_PATH}
+ *
+ * @return <code>true</code> if request has been sent, <code>false</code>
+ * otherwise
+ */
+ public boolean setFolderRoot() {
+ if (mObexSession == null) {
+ return false;
+ }
+
+ BluetoothMasRequest request = new BluetoothMasRequestSetPath(true);
+ return mObexSession.makeRequest(request);
+ }
+
+ /**
+ * Goes back to parent folder in folder hierarchy
+ * <p>
+ * Upon completion callback handler will receive {@link #EVENT_SET_PATH}
+ *
+ * @return <code>true</code> if request has been sent, <code>false</code>
+ * otherwise
+ */
+ public boolean setFolderUp() {
+ if (mObexSession == null) {
+ return false;
+ }
+
+ BluetoothMasRequest request = new BluetoothMasRequestSetPath(false);
+ return mObexSession.makeRequest(request);
+ }
+
+ /**
+ * Goes down to specified sub-folder in folder hierarchy
+ * <p>
+ * Upon completion callback handler will receive {@link #EVENT_SET_PATH}
+ *
+ * @param name name of sub-folder
+ * @return <code>true</code> if request has been sent, <code>false</code>
+ * otherwise
+ */
+ public boolean setFolderDown(String name) {
+ if (mObexSession == null) {
+ return false;
+ }
+
+ if (name == null || name.isEmpty() || name.contains("/")) {
+ return false;
+ }
+
+ BluetoothMasRequest request = new BluetoothMasRequestSetPath(name);
+ return mObexSession.makeRequest(request);
+ }
+
+ /**
+ * Gets current path in folder hierarchy
+ *
+ * @return current path
+ */
+ public String getCurrentPath() {
+ if (mPath.size() == 0) {
+ return "";
+ }
+
+ Iterator<String> iter = mPath.iterator();
+
+ StringBuilder sb = new StringBuilder(iter.next());
+
+ while (iter.hasNext()) {
+ sb.append("/").append(iter.next());
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Gets list of sub-folders in current folder
+ * <p>
+ * Upon completion callback handler will receive
+ * {@link #EVENT_GET_FOLDER_LISTING}
+ *
+ * @return <code>true</code> if request has been sent, <code>false</code>
+ * otherwise
+ */
+ public boolean getFolderListing() {
+ return getFolderListing((short) 0, (short) 0);
+ }
+
+ /**
+ * Gets list of sub-folders in current folder
+ * <p>
+ * Upon completion callback handler will receive
+ * {@link #EVENT_GET_FOLDER_LISTING}
+ *
+ * @param maxListCount maximum number of items returned or <code>0</code>
+ * for default value
+ * @param listStartOffset index of first item returned or <code>0</code> for
+ * default value
+ * @return <code>true</code> if request has been sent, <code>false</code>
+ * otherwise
+ * @throws IllegalArgumentException if either maxListCount or
+ * listStartOffset are outside allowed range [0..65535]
+ */
+ public boolean getFolderListing(int maxListCount, int listStartOffset) {
+ if (mObexSession == null) {
+ return false;
+ }
+
+ BluetoothMasRequest request = new BluetoothMasRequestGetFolderListing(maxListCount,
+ listStartOffset);
+ return mObexSession.makeRequest(request);
+ }
+
+ /**
+ * Gets number of sub-folders in current folder
+ * <p>
+ * Upon completion callback handler will receive
+ * {@link #EVENT_GET_FOLDER_LISTING_SIZE}
+ *
+ * @return <code>true</code> if request has been sent, <code>false</code>
+ * otherwise
+ */
+ public boolean getFolderListingSize() {
+ if (mObexSession == null) {
+ return false;
+ }
+
+ BluetoothMasRequest request = new BluetoothMasRequestGetFolderListingSize();
+ return mObexSession.makeRequest(request);
+ }
+
+ /**
+ * Gets list of messages in specified sub-folder
+ * <p>
+ * Upon completion callback handler will receive
+ * {@link #EVENT_GET_MESSAGES_LISTING}
+ *
+ * @param folder name of sub-folder or <code>null</code> for current folder
+ * @param parameters bit-mask specifying requested parameters in listing or
+ * <code>0</code> for default value
+ * @return <code>true</code> if request has been sent, <code>false</code>
+ * otherwise
+ */
+ public boolean getMessagesListing(String folder, int parameters) {
+ return getMessagesListing(folder, parameters, null, (byte) 0, 0, 0);
+ }
+
+ /**
+ * Gets list of messages in specified sub-folder
+ * <p>
+ * Upon completion callback handler will receive
+ * {@link #EVENT_GET_MESSAGES_LISTING}
+ *
+ * @param folder name of sub-folder or <code>null</code> for current folder
+ * @param parameters corresponds to <code>ParameterMask</code> application
+ * parameter in MAP specification
+ * @param filter {@link MessagesFilter} object describing filters to be
+ * applied on listing by MSE
+ * @param subjectLength maximum length of message subject in returned
+ * listing or <code>0</code> for default value
+ * @return <code>true</code> if request has been sent, <code>false</code>
+ * otherwise
+ * @throws IllegalArgumentException if subjectLength is outside allowed
+ * range [0..255]
+ */
+ public boolean getMessagesListing(String folder, int parameters, MessagesFilter filter,
+ int subjectLength) {
+
+ return getMessagesListing(folder, parameters, filter, subjectLength, 0, 0);
+ }
+
+ /**
+ * Gets list of messages in specified sub-folder
+ * <p>
+ * Upon completion callback handler will receive
+ * {@link #EVENT_GET_MESSAGES_LISTING}
+ *
+ * @param folder name of sub-folder or <code>null</code> for current folder
+ * @param parameters corresponds to <code>ParameterMask</code> application
+ * parameter in MAP specification
+ * @param filter {@link MessagesFilter} object describing filters to be
+ * applied on listing by MSE
+ * @param subjectLength maximum length of message subject in returned
+ * listing or <code>0</code> for default value
+ * @param maxListCount maximum number of items returned or <code>0</code>
+ * for default value
+ * @param listStartOffset index of first item returned or <code>0</code> for
+ * default value
+ * @return <code>true</code> if request has been sent, <code>false</code>
+ * otherwise
+ * @throws IllegalArgumentException if subjectLength is outside allowed
+ * range [0..255] or either maxListCount or listStartOffset are
+ * outside allowed range [0..65535]
+ */
+ public boolean getMessagesListing(String folder, int parameters, MessagesFilter filter,
+ int subjectLength, int maxListCount, int listStartOffset) {
+
+ if (mObexSession == null) {
+ return false;
+ }
+
+ BluetoothMasRequest request = new BluetoothMasRequestGetMessagesListing(folder,
+ parameters, filter, subjectLength, maxListCount, listStartOffset);
+ return mObexSession.makeRequest(request);
+ }
+
+ /**
+ * Gets number of messages in current folder
+ * <p>
+ * Upon completion callback handler will receive
+ * {@link #EVENT_GET_MESSAGES_LISTING_SIZE}
+ *
+ * @return <code>true</code> if request has been sent, <code>false</code>
+ * otherwise
+ */
+ public boolean getMessagesListingSize() {
+ if (mObexSession == null) {
+ return false;
+ }
+
+ BluetoothMasRequest request = new BluetoothMasRequestGetMessagesListingSize();
+ return mObexSession.makeRequest(request);
+ }
+
+ /**
+ * Retrieves message from MSE
+ * <p>
+ * Upon completion callback handler will receive {@link #EVENT_GET_MESSAGE}
+ *
+ * @param handle handle of message to retrieve
+ * @param charset {@link CharsetType} object corresponding to
+ * <code>Charset</code> application parameter in MAP
+ * specification
+ * @param attachment corresponds to <code>Attachment</code> application
+ * parameter in MAP specification
+ * @return <code>true</code> if request has been sent, <code>false</code>
+ * otherwise
+ */
+ public boolean getMessage(String handle, CharsetType charset, boolean attachment) {
+ if (mObexSession == null) {
+ return false;
+ }
+
+ try {
+ /* just to validate */
+ new BigInteger(handle, 16);
+ } catch (NumberFormatException e) {
+ return false;
+ }
+
+ BluetoothMasRequest request = new BluetoothMasRequestGetMessage(handle, charset,
+ attachment);
+ return mObexSession.makeRequest(request);
+ }
+
+ /**
+ * Sets read status of message on MSE
+ * <p>
+ * Upon completion callback handler will receive
+ * {@link #EVENT_SET_MESSAGE_STATUS}
+ *
+ * @param handle handle of message
+ * @param read <code>true</code> for "read", <code>false</code> for "unread"
+ * @return <code>true</code> if request has been sent, <code>false</code>
+ * otherwise
+ */
+ public boolean setMessageReadStatus(String handle, boolean read) {
+ if (mObexSession == null) {
+ return false;
+ }
+
+ try {
+ /* just to validate */
+ new BigInteger(handle, 16);
+ } catch (NumberFormatException e) {
+ return false;
+ }
+
+ BluetoothMasRequest request = new BluetoothMasRequestSetMessageStatus(handle,
+ StatusIndicator.READ, read);
+ return mObexSession.makeRequest(request);
+ }
+
+ /**
+ * Sets deleted status of message on MSE
+ * <p>
+ * Upon completion callback handler will receive
+ * {@link #EVENT_SET_MESSAGE_STATUS}
+ *
+ * @param handle handle of message
+ * @param deleted <code>true</code> for "deleted", <code>false</code> for
+ * "undeleted"
+ * @return <code>true</code> if request has been sent, <code>false</code>
+ * otherwise
+ */
+ public boolean setMessageDeletedStatus(String handle, boolean deleted) {
+ if (mObexSession == null) {
+ return false;
+ }
+
+ try {
+ /* just to validate */
+ new BigInteger(handle, 16);
+ } catch (NumberFormatException e) {
+ return false;
+ }
+
+ BluetoothMasRequest request = new BluetoothMasRequestSetMessageStatus(handle,
+ StatusIndicator.DELETED, deleted);
+ return mObexSession.makeRequest(request);
+ }
+
+ /**
+ * Pushes new message to MSE
+ * <p>
+ * Upon completion callback handler will receive {@link #EVENT_PUSH_MESSAGE}
+ *
+ * @param folder name of sub-folder to push to or <code>null</code> for
+ * current folder
+ * @param charset {@link CharsetType} object corresponding to
+ * <code>Charset</code> application parameter in MAP
+ * specification
+ * @return <code>true</code> if request has been sent, <code>false</code>
+ * otherwise
+ */
+ public boolean pushMessage(String folder, BluetoothMapBmessage bmsg, CharsetType charset) {
+ return pushMessage(folder, bmsg, charset, false, false);
+ }
+
+ /**
+ * Pushes new message to MSE
+ * <p>
+ * Upon completion callback handler will receive {@link #EVENT_PUSH_MESSAGE}
+ *
+ * @param folder name of sub-folder to push to or <code>null</code> for
+ * current folder
+ * @param bmsg {@link BluetoothMapBmessage} object representing message to
+ * be pushed
+ * @param charset {@link CharsetType} object corresponding to
+ * <code>Charset</code> application parameter in MAP
+ * specification
+ * @param transparent corresponds to <code>Transparent</code> application
+ * parameter in MAP specification
+ * @param retry corresponds to <code>Transparent</code> application
+ * parameter in MAP specification
+ * @return <code>true</code> if request has been sent, <code>false</code>
+ * otherwise
+ */
+ public boolean pushMessage(String folder, BluetoothMapBmessage bmsg, CharsetType charset,
+ boolean transparent, boolean retry) {
+ if (mObexSession == null) {
+ return false;
+ }
+
+ String bmsgString = BluetoothMapBmessageBuilder.createBmessage(bmsg);
+
+ BluetoothMasRequest request =
+ new BluetoothMasRequestPushMessage(folder, bmsgString, charset, transparent, retry);
+ return mObexSession.makeRequest(request);
+ }
+
+ /**
+ * Requests MSE to initiate ubdate of inbox
+ * <p>
+ * Upon completion callback handler will receive {@link #EVENT_UPDATE_INBOX}
+ *
+ * @return <code>true</code> if request has been sent, <code>false</code>
+ * otherwise
+ */
+ public boolean updateInbox() {
+ if (mObexSession == null) {
+ return false;
+ }
+
+ BluetoothMasRequest request = new BluetoothMasRequestUpdateInbox();
+ return mObexSession.makeRequest(request);
+ }
+}