/* * 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.bluetooth.SdpMasRecord; 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 *

* arg1 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 *

* obj 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 *

* obj 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 *

* obj 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 *

* obj 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 *

* obj 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 *

* obj 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 *

* obj contains 1 if notifications are enabled and * 0 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 *

* obj contains {@link BluetoothMapEventReport} * * @see #setNotificationRegistration(boolean) */ public static final int EVENT_EVENT_REPORT = 11; /** * Callback message sent when messages listing size is received *

* obj 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 ParameterMask application * parameter value in MAP specification */ public static final int PARAMETER_DEFAULT = 0x00000000; /** * Constant corresponding to ParameterMask application * parameter value in MAP specification */ public static final int PARAMETER_SUBJECT = 0x00000001; /** * Constant corresponding to ParameterMask application * parameter value in MAP specification */ public static final int PARAMETER_DATETIME = 0x00000002; /** * Constant corresponding to ParameterMask application * parameter value in MAP specification */ public static final int PARAMETER_SENDER_NAME = 0x00000004; /** * Constant corresponding to ParameterMask application * parameter value in MAP specification */ public static final int PARAMETER_SENDER_ADDRESSING = 0x00000008; /** * Constant corresponding to ParameterMask application * parameter value in MAP specification */ public static final int PARAMETER_RECIPIENT_NAME = 0x00000010; /** * Constant corresponding to ParameterMask application * parameter value in MAP specification */ public static final int PARAMETER_RECIPIENT_ADDRESSING = 0x00000020; /** * Constant corresponding to ParameterMask application * parameter value in MAP specification */ public static final int PARAMETER_TYPE = 0x00000040; /** * Constant corresponding to ParameterMask application * parameter value in MAP specification */ public static final int PARAMETER_SIZE = 0x00000080; /** * Constant corresponding to ParameterMask application * parameter value in MAP specification */ public static final int PARAMETER_RECEPTION_STATUS = 0x00000100; /** * Constant corresponding to ParameterMask application * parameter value in MAP specification */ public static final int PARAMETER_TEXT = 0x00000200; /** * Constant corresponding to ParameterMask application * parameter value in MAP specification */ public static final int PARAMETER_ATTACHMENT_SIZE = 0x00000400; /** * Constant corresponding to ParameterMask application * parameter value in MAP specification */ public static final int PARAMETER_PRIORITY = 0x00000800; /** * Constant corresponding to ParameterMask application * parameter value in MAP specification */ public static final int PARAMETER_READ = 0x00001000; /** * Constant corresponding to ParameterMask application * parameter value in MAP specification */ public static final int PARAMETER_SENT = 0x00002000; /** * Constant corresponding to ParameterMask application * parameter value in MAP specification */ public static final int PARAMETER_PROTECTED = 0x00004000; /** * Constant corresponding to ParameterMask 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 SdpMasRecord 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 mPath = null; private static class SessionHandler extends Handler { private final WeakReference mClient; public SessionHandler(BluetoothMasClient client) { super(); mClient = new WeakReference(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 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 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) { // Send event, status and notification state for both sucess and failure case. mCallback.obtainMessage(event, success ? STATUS_OK : STATUS_FAILED, mMas.getMasInstanceId(), param).sendToTarget(); } private class SocketConnectThread extends Thread { private BluetoothSocket socket = null; public SocketConnectThread() { super("SocketConnectThread"); } @Override public void run() { try { socket = mDevice.createRfcommSocket(mMas.getRfcommCannelNumber()); 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) { //Handle possible NPE for obexTime constructor utility if(filterBegin != null ) periodBegin = (new ObexTime(filterBegin)).toString(); if(filterEnd != null) 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 arg1 set to either * {@link #STATUS_OK} or {@link #STATUS_FAILED} and * arg2 to MAS instance ID. obj in * message is event specific. */ public BluetoothMasClient(BluetoothDevice device, SdpMasRecord mas, Handler callback) { mDevice = device; mMas = mas; mCallback = callback; mPath = new ArrayDeque(); } /** * Retrieves MAS instance data associated with client * * @return instance data object */ public SdpMasRecord getInstanceData() { return mMas; } /** * Connects to MAS instance *

* 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 *

* 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.getMasInstanceId(), mSessionHandler); BluetoothMasRequest request = new BluetoothMasRequestSetNotificationRegistration(true); return mObexSession.makeRequest(request); } private boolean disableNotifications() { Log.v(TAG, "enableNotifications()"); if (mMnsService != null) { mMnsService.unregisterCallback(mMas.getMasInstanceId()); } mMnsService = null; BluetoothMasRequest request = new BluetoothMasRequestSetNotificationRegistration(false); return mObexSession.makeRequest(request); } /** * Sets state of notifications for MAS instance *

* Once notifications are enabled, callback handler will receive * {@link #EVENT_EVENT_REPORT} when new notification is received *

* Upon completion callback handler will receive * {@link #EVENT_SET_NOTIFICATION_REGISTRATION} * * @param status true if notifications shall be enabled, * false otherwise * @return true if request has been sent, false * 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 true if notifications are enabled, * false otherwise */ public boolean getNotificationRegistration() { return mNotificationEnabled; } /** * Goes back to root of folder hierarchy *

* Upon completion callback handler will receive {@link #EVENT_SET_PATH} * * @return true if request has been sent, false * 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 *

* Upon completion callback handler will receive {@link #EVENT_SET_PATH} * * @return true if request has been sent, false * 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 *

* Upon completion callback handler will receive {@link #EVENT_SET_PATH} * * @param name name of sub-folder * @return true if request has been sent, false * 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 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 *

* Upon completion callback handler will receive * {@link #EVENT_GET_FOLDER_LISTING} * * @return true if request has been sent, false * otherwise */ public boolean getFolderListing() { return getFolderListing((short) 0, (short) 0); } /** * Gets list of sub-folders in current folder *

* Upon completion callback handler will receive * {@link #EVENT_GET_FOLDER_LISTING} * * @param maxListCount maximum number of items returned or 0 * for default value * @param listStartOffset index of first item returned or 0 for * default value * @return true if request has been sent, false * 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 *

* Upon completion callback handler will receive * {@link #EVENT_GET_FOLDER_LISTING_SIZE} * * @return true if request has been sent, false * 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 *

* Upon completion callback handler will receive * {@link #EVENT_GET_MESSAGES_LISTING} * * @param folder name of sub-folder or null for current folder * @param parameters bit-mask specifying requested parameters in listing or * 0 for default value * @return true if request has been sent, false * 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 *

* Upon completion callback handler will receive * {@link #EVENT_GET_MESSAGES_LISTING} * * @param folder name of sub-folder or null for current folder * @param parameters corresponds to ParameterMask 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 0 for default value * @return true if request has been sent, false * 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 *

* Upon completion callback handler will receive * {@link #EVENT_GET_MESSAGES_LISTING} * * @param folder name of sub-folder or null for current folder * @param parameters corresponds to ParameterMask 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 0 for default value * @param maxListCount maximum number of items returned or 0 * for default value * @param listStartOffset index of first item returned or 0 for * default value * @return true if request has been sent, false * 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 *

* Upon completion callback handler will receive * {@link #EVENT_GET_MESSAGES_LISTING_SIZE} * * @return true if request has been sent, false * otherwise */ public boolean getMessagesListingSize() { if (mObexSession == null) { return false; } BluetoothMasRequest request = new BluetoothMasRequestGetMessagesListingSize(); return mObexSession.makeRequest(request); } /** * Retrieves message from MSE *

* Upon completion callback handler will receive {@link #EVENT_GET_MESSAGE} * * @param handle handle of message to retrieve * @param charset {@link CharsetType} object corresponding to * Charset application parameter in MAP * specification * @param attachment corresponds to Attachment application * parameter in MAP specification * @return true if request has been sent, false * otherwise */ public boolean getMessage(String handle, boolean attachment) { if (mObexSession == null) { return false; } try { /* just to validate */ new BigInteger(handle, 16); } catch (NumberFormatException e) { return false; } // Since we support only text messaging via Bluetooth, it is OK to restrict the requests to // force conversion to UTF-8. BluetoothMasRequest request = new BluetoothMasRequestGetMessage(handle, CharsetType.UTF_8, attachment); return mObexSession.makeRequest(request); } /** * Sets read status of message on MSE *

* Upon completion callback handler will receive * {@link #EVENT_SET_MESSAGE_STATUS} * * @param handle handle of message * @param read true for "read", false for "unread" * @return true if request has been sent, false * 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 *

* Upon completion callback handler will receive * {@link #EVENT_SET_MESSAGE_STATUS} * * @param handle handle of message * @param deleted true for "deleted", false for * "undeleted" * @return true if request has been sent, false * 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 *

* Upon completion callback handler will receive {@link #EVENT_PUSH_MESSAGE} * * @param folder name of sub-folder to push to or null for * current folder * @param charset {@link CharsetType} object corresponding to * Charset application parameter in MAP * specification * @return true if request has been sent, false * otherwise */ public boolean pushMessage(String folder, BluetoothMapBmessage bmsg, CharsetType charset) { return pushMessage(folder, bmsg, charset, false, false); } /** * Pushes new message to MSE *

* Upon completion callback handler will receive {@link #EVENT_PUSH_MESSAGE} * * @param folder name of sub-folder to push to or null for * current folder * @param bmsg {@link BluetoothMapBmessage} object representing message to * be pushed * @param charset {@link CharsetType} object corresponding to * Charset application parameter in MAP * specification * @param transparent corresponds to Transparent application * parameter in MAP specification * @param retry corresponds to Transparent application * parameter in MAP specification * @return true if request has been sent, false * 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 *

* Upon completion callback handler will receive {@link #EVENT_UPDATE_INBOX} * * @return true if request has been sent, false * otherwise */ public boolean updateInbox() { if (mObexSession == null) { return false; } BluetoothMasRequest request = new BluetoothMasRequestUpdateInbox(); return mObexSession.makeRequest(request); } }