| /* |
| * Copyright (C) 2015 Samsung System LSI |
| * 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 com.android.bluetooth.map; |
| |
| import android.content.ContentProviderClient; |
| import android.content.ContentResolver; |
| import android.content.Context; |
| import android.database.Cursor; |
| import android.net.Uri; |
| import android.os.Bundle; |
| import android.os.Handler; |
| import android.os.Message; |
| import android.os.ParcelUuid; |
| import android.os.RemoteException; |
| import android.os.UserManager; |
| import android.telephony.TelephonyManager; |
| import android.text.format.DateUtils; |
| import android.util.Log; |
| |
| import com.android.bluetooth.SignedLongLong; |
| import com.android.bluetooth.map.BluetoothMapUtils.TYPE; |
| import com.android.bluetooth.mapapi.BluetoothMapContract; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.text.ParseException; |
| import java.util.Arrays; |
| import java.util.Calendar; |
| |
| import javax.obex.HeaderSet; |
| import javax.obex.Operation; |
| import javax.obex.ResponseCodes; |
| import javax.obex.ServerRequestHandler; |
| |
| |
| public class BluetoothMapObexServer extends ServerRequestHandler { |
| |
| private static final String TAG = "BluetoothMapObexServer"; |
| |
| private static final boolean D = BluetoothMapService.DEBUG; |
| private static final boolean V = BluetoothMapService.VERBOSE; |
| |
| private static final int UUID_LENGTH = 16; |
| |
| private static final long PROVIDER_ANR_TIMEOUT = 20 * DateUtils.SECOND_IN_MILLIS; |
| |
| /* OBEX header and value used to detect clients that support threadId in the message listing. */ |
| private static final int THREADED_MAIL_HEADER_ID = 0xFA; |
| private static final long THREAD_MAIL_KEY = 0x534c5349; |
| |
| // 128 bit UUID for MAP |
| private static final byte[] MAP_TARGET = new byte[]{ |
| (byte) 0xBB, |
| (byte) 0x58, |
| (byte) 0x2B, |
| (byte) 0x40, |
| (byte) 0x42, |
| (byte) 0x0C, |
| (byte) 0x11, |
| (byte) 0xDB, |
| (byte) 0xB0, |
| (byte) 0xDE, |
| (byte) 0x08, |
| (byte) 0x00, |
| (byte) 0x20, |
| (byte) 0x0C, |
| (byte) 0x9A, |
| (byte) 0x66 |
| }; |
| public static final ParcelUuid MAP = |
| ParcelUuid.fromString("00001134-0000-1000-8000-00805F9B34FB"); |
| public static final ParcelUuid MNS = |
| ParcelUuid.fromString("00001133-0000-1000-8000-00805F9B34FB"); |
| public static final ParcelUuid MAS = |
| ParcelUuid.fromString("00001132-0000-1000-8000-00805F9B34FB"); |
| /* Message types */ |
| private static final String TYPE_GET_FOLDER_LISTING = "x-obex/folder-listing"; |
| private static final String TYPE_GET_MESSAGE_LISTING = "x-bt/MAP-msg-listing"; |
| private static final String TYPE_GET_CONVO_LISTING = "x-bt/MAP-convo-listing"; |
| private static final String TYPE_MESSAGE = "x-bt/message"; |
| private static final String TYPE_SET_MESSAGE_STATUS = "x-bt/messageStatus"; |
| private static final String TYPE_SET_NOTIFICATION_REGISTRATION = |
| "x-bt/MAP-NotificationRegistration"; |
| private static final String TYPE_MESSAGE_UPDATE = "x-bt/MAP-messageUpdate"; |
| private static final String TYPE_GET_MAS_INSTANCE_INFORMATION = "x-bt/MASInstanceInformation"; |
| private static final String TYPE_SET_OWNER_STATUS = "x-bt/participant"; |
| private static final String TYPE_SET_NOTIFICATION_FILTER = "x-bt/MAP-notification-filter"; |
| |
| private static final int MAS_INSTANCE_INFORMATION_LENGTH = 200; |
| |
| private BluetoothMapFolderElement mCurrentFolder; |
| private BluetoothMapContentObserver mObserver = null; |
| private Handler mCallback = null; |
| private Context mContext; |
| private boolean mIsAborted = false; |
| BluetoothMapContent mOutContent; |
| private String mBaseUriString = null; |
| private long mAccountId = 0; |
| private BluetoothMapAccountItem mAccount = null; |
| private Uri mEmailFolderUri = null; |
| private int mMasId = 0; |
| private BluetoothMapMasInstance mMasInstance; // TODO: change to interface? |
| // updated during connect if remote has alternative value |
| private int mRemoteFeatureMask = BluetoothMapUtils.MAP_FEATURE_DEFAULT_BITMASK; |
| private boolean mEnableSmsMms = false; |
| private boolean mThreadIdSupport = false; // true if peer supports threadId in msg listing |
| // Defaults message version is 1.0 but 1.1+ if feature bit is set |
| private String mMessageVersion = BluetoothMapUtils.MAP_V10_STR; |
| private String mAuthority; |
| private ContentResolver mResolver; |
| private ContentProviderClient mProviderClient = null; |
| |
| public BluetoothMapObexServer(Handler callback, Context context, |
| BluetoothMapContentObserver observer, BluetoothMapMasInstance mas, |
| BluetoothMapAccountItem account, boolean enableSmsMms) throws RemoteException { |
| super(); |
| mCallback = callback; |
| mContext = context; |
| mObserver = observer; |
| mEnableSmsMms = enableSmsMms; |
| mAccount = account; |
| mMasId = mas.getMasId(); |
| mMasInstance = mas; |
| mRemoteFeatureMask = mMasInstance.getRemoteFeatureMask(); |
| |
| if (account != null && account.getProviderAuthority() != null) { |
| mAccountId = account.getAccountId(); |
| mAuthority = account.getProviderAuthority(); |
| mResolver = mContext.getContentResolver(); |
| if (D) { |
| Log.d(TAG, "BluetoothMapObexServer(): accountId=" + mAccountId); |
| } |
| mBaseUriString = account.mBase_uri + "/"; |
| if (D) { |
| Log.d(TAG, "BluetoothMapObexServer(): baseUri=" + mBaseUriString); |
| } |
| if (account.getType() == TYPE.EMAIL) { |
| mEmailFolderUri = |
| BluetoothMapContract.buildFolderUri(mAuthority, Long.toString(mAccountId)); |
| if (D) { |
| Log.d(TAG, "BluetoothMapObexServer(): mEmailFolderUri=" + mEmailFolderUri); |
| } |
| } |
| mProviderClient = acquireUnstableContentProviderOrThrow(); |
| } |
| |
| buildFolderStructure(); /* Build the default folder structure, and set |
| mCurrentFolder to root folder */ |
| mObserver.setFolderStructure(mCurrentFolder.getRoot()); |
| |
| mOutContent = new BluetoothMapContent(mContext, mAccount, mMasInstance); |
| |
| } |
| |
| /** |
| * |
| */ |
| private ContentProviderClient acquireUnstableContentProviderOrThrow() throws RemoteException { |
| ContentProviderClient providerClient = |
| mResolver.acquireUnstableContentProviderClient(mAuthority); |
| if (providerClient == null) { |
| throw new RemoteException("Failed to acquire provider for " + mAuthority); |
| } |
| providerClient.setDetectNotResponding(PROVIDER_ANR_TIMEOUT); |
| return providerClient; |
| } |
| |
| /** |
| * Build the default minimal folder structure, as defined in the MAP specification. |
| */ |
| private void buildFolderStructure() throws RemoteException { |
| //This will be the root element |
| mCurrentFolder = new BluetoothMapFolderElement("root", null); |
| mCurrentFolder.setHasSmsMmsContent(mEnableSmsMms); |
| boolean hasIM = false; |
| boolean hasEmail = false; |
| if (mAccount != null) { |
| if (mAccount.getType() == TYPE.IM) { |
| hasIM = true; |
| } |
| if (mAccount.getType() == TYPE.EMAIL) { |
| hasEmail = true; |
| } |
| } |
| mCurrentFolder.setHasImContent(hasIM); |
| mCurrentFolder.setHasEmailContent(hasEmail); |
| |
| BluetoothMapFolderElement tmpFolder; |
| tmpFolder = mCurrentFolder.addFolder("telecom"); // root/telecom |
| tmpFolder.setHasSmsMmsContent(mEnableSmsMms); |
| tmpFolder.setHasImContent(hasIM); |
| tmpFolder.setHasEmailContent(hasEmail); |
| |
| tmpFolder = tmpFolder.addFolder("msg"); // root/telecom/msg |
| tmpFolder.setHasSmsMmsContent(mEnableSmsMms); |
| tmpFolder.setHasImContent(hasIM); |
| tmpFolder.setHasEmailContent(hasEmail); |
| |
| // Add the mandatory folders |
| addBaseFolders(tmpFolder); |
| if (mEnableSmsMms) { |
| addSmsMmsFolders(tmpFolder); |
| } |
| if (hasEmail) { |
| if (D) { |
| Log.d(TAG, "buildFolderStructure(): " + mEmailFolderUri.toString()); |
| } |
| addEmailFolders(tmpFolder); |
| } |
| if (hasIM) { |
| addImFolders(tmpFolder); |
| } |
| } |
| |
| /** |
| * Add base (Inbox/Outbox/Sent/Deleted) |
| * @param root |
| */ |
| private void addBaseFolders(BluetoothMapFolderElement root) { |
| root.addFolder(BluetoothMapContract.FOLDER_NAME_INBOX); // root/telecom/msg/inbox |
| root.addFolder(BluetoothMapContract.FOLDER_NAME_OUTBOX); |
| root.addFolder(BluetoothMapContract.FOLDER_NAME_SENT); |
| root.addFolder(BluetoothMapContract.FOLDER_NAME_DELETED); |
| } |
| |
| /** |
| * Add SMS / MMS Base folders |
| * @param root |
| */ |
| private void addSmsMmsFolders(BluetoothMapFolderElement root) { |
| root.addSmsMmsFolder(BluetoothMapContract.FOLDER_NAME_INBOX); // root/telecom/msg/inbox |
| root.addSmsMmsFolder(BluetoothMapContract.FOLDER_NAME_OUTBOX); |
| root.addSmsMmsFolder(BluetoothMapContract.FOLDER_NAME_SENT); |
| root.addSmsMmsFolder(BluetoothMapContract.FOLDER_NAME_DELETED); |
| root.addSmsMmsFolder(BluetoothMapContract.FOLDER_NAME_DRAFT); |
| } |
| |
| |
| private void addImFolders(BluetoothMapFolderElement root) throws RemoteException { |
| // Select all parent folders |
| root.addImFolder(BluetoothMapContract.FOLDER_NAME_INBOX, |
| BluetoothMapContract.FOLDER_ID_INBOX); // root/telecom/msg/inbox |
| root.addImFolder(BluetoothMapContract.FOLDER_NAME_OUTBOX, |
| BluetoothMapContract.FOLDER_ID_OUTBOX); |
| root.addImFolder(BluetoothMapContract.FOLDER_NAME_SENT, |
| BluetoothMapContract.FOLDER_ID_SENT); |
| root.addImFolder(BluetoothMapContract.FOLDER_NAME_DELETED, |
| BluetoothMapContract.FOLDER_ID_DELETED); |
| root.addImFolder(BluetoothMapContract.FOLDER_NAME_DRAFT, |
| BluetoothMapContract.FOLDER_ID_DRAFT); |
| } |
| |
| /** |
| * Recursively adds folders based on the folders in the email content provider. |
| * Add a content observer? - to refresh the folder list if any change occurs. |
| * Consider simply deleting the entire table, and then rebuild using |
| * buildFolderStructure() |
| * WARNING: there is no way to notify the client about these changes - hence |
| * we need to either keep the folder structure constant, disconnect or fail anything |
| * referring to currentFolder. |
| * It is unclear what to set as current folder to be able to go one level up... |
| * The best solution would be to keep the folder structure constant during a connection. |
| * @param folder the parent folder to which subFolders needs to be added. The |
| * folder.getFolderId() will be used to query sub-folders. |
| * Use a parentFolder with id -1 to get all folders from root. |
| */ |
| private void addEmailFolders(BluetoothMapFolderElement parentFolder) throws RemoteException { |
| // Select all parent folders |
| BluetoothMapFolderElement newFolder; |
| |
| String where = BluetoothMapContract.FolderColumns.PARENT_FOLDER_ID + " = " |
| + parentFolder.getFolderId(); |
| Cursor c = mProviderClient.query(mEmailFolderUri, BluetoothMapContract.BT_FOLDER_PROJECTION, |
| where, null, null); |
| try { |
| if (c != null) { |
| c.moveToPosition(-1); |
| while (c.moveToNext()) { |
| String name = |
| c.getString(c.getColumnIndex(BluetoothMapContract.FolderColumns.NAME)); |
| long id = c.getLong(c.getColumnIndex(BluetoothMapContract.FolderColumns._ID)); |
| newFolder = parentFolder.addEmailFolder(name, id); |
| addEmailFolders(newFolder); // Use recursion to add any sub folders |
| } |
| |
| } else { |
| if (D) { |
| Log.d(TAG, "addEmailFolders(): no elements found"); |
| } |
| } |
| } finally { |
| if (c != null) { |
| c.close(); |
| } |
| } |
| } |
| |
| @Override |
| public boolean isSrmSupported() { |
| // TODO: Update based on the transport used |
| return true; |
| } |
| |
| public int getRemoteFeatureMask() { |
| return mRemoteFeatureMask; |
| } |
| |
| public void setRemoteFeatureMask(int mRemoteFeatureMask) { |
| if (D) { |
| Log.d(TAG, "setRemoteFeatureMask() " + Integer.toHexString(mRemoteFeatureMask)); |
| } |
| this.mRemoteFeatureMask = mRemoteFeatureMask; |
| this.mOutContent.setRemoteFeatureMask(mRemoteFeatureMask); |
| if ((mRemoteFeatureMask & BluetoothMapUtils.MAP_FEATURE_MESSAGE_FORMAT_V11_BIT) |
| == BluetoothMapUtils.MAP_FEATURE_MESSAGE_FORMAT_V11_BIT) { |
| mMessageVersion = BluetoothMapUtils.MAP_V11_STR; |
| } |
| if (V) Log.d(TAG," setRemoteFeatureMask mMessageVersion :" + mMessageVersion); |
| } |
| |
| @Override |
| public int onConnect(final HeaderSet request, HeaderSet reply) { |
| if (D) { |
| Log.d(TAG, "onConnect():"); |
| } |
| if (V) { |
| logHeader(request); |
| } |
| mThreadIdSupport = false; // Always assume not supported at new connect. |
| //always assume version 1.0 to start with |
| mMessageVersion = BluetoothMapUtils.MAP_V10_STR; |
| notifyUpdateWakeLock(); |
| Long threadedMailKey = null; |
| try { |
| byte[] uuid = (byte[]) request.getHeader(HeaderSet.TARGET); |
| threadedMailKey = (Long) request.getHeader(THREADED_MAIL_HEADER_ID); |
| if (uuid == null) { |
| return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE; |
| } |
| if (D) { |
| Log.d(TAG, "onConnect(): uuid=" + Arrays.toString(uuid)); |
| } |
| |
| if (uuid.length != UUID_LENGTH) { |
| Log.w(TAG, "Wrong UUID length"); |
| return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE; |
| } |
| for (int i = 0; i < UUID_LENGTH; i++) { |
| if (uuid[i] != MAP_TARGET[i]) { |
| Log.w(TAG, "Wrong UUID"); |
| return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE; |
| } |
| } |
| reply.setHeader(HeaderSet.WHO, uuid); |
| } catch (IOException e) { |
| Log.e(TAG, "Exception during onConnect:", e); |
| return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR; |
| } |
| |
| try { |
| byte[] remote = (byte[]) request.getHeader(HeaderSet.WHO); |
| if (remote != null) { |
| if (D) { |
| Log.d(TAG, "onConnect(): remote=" + Arrays.toString(remote)); |
| } |
| reply.setHeader(HeaderSet.TARGET, remote); |
| } |
| if (threadedMailKey != null && threadedMailKey.longValue() == THREAD_MAIL_KEY) { |
| /* If the client provides the correct key we enable threaded e-mail support |
| * and reply to the client that we support the requested feature. |
| * This is currently an Android only feature. */ |
| mThreadIdSupport = true; |
| reply.setHeader(THREADED_MAIL_HEADER_ID, THREAD_MAIL_KEY); |
| } |
| } catch (IOException e) { |
| Log.e(TAG, "Exception during onConnect:", e); |
| mThreadIdSupport = false; |
| return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR; |
| } |
| |
| if ((mRemoteFeatureMask & BluetoothMapUtils.MAP_FEATURE_MESSAGE_LISTING_FORMAT_V11_BIT) |
| == BluetoothMapUtils.MAP_FEATURE_MESSAGE_LISTING_FORMAT_V11_BIT) { |
| mThreadIdSupport = true; |
| } |
| |
| if ((mRemoteFeatureMask & BluetoothMapUtils.MAP_FEATURE_MESSAGE_FORMAT_V11_BIT) |
| == BluetoothMapUtils.MAP_FEATURE_MESSAGE_FORMAT_V11_BIT) { |
| mMessageVersion = BluetoothMapUtils.MAP_V11_STR; |
| } |
| |
| if (V) { |
| Log.v(TAG, "onConnect(): uuid is ok, will send out " + "MSG_SESSION_ESTABLISHED msg."); |
| } |
| |
| if (mCallback != null) { |
| Message msg = Message.obtain(mCallback); |
| msg.what = BluetoothMapService.MSG_SESSION_ESTABLISHED; |
| msg.sendToTarget(); |
| } |
| |
| return ResponseCodes.OBEX_HTTP_OK; |
| } |
| |
| @Override |
| public void onDisconnect(final HeaderSet req, final HeaderSet resp) { |
| if (D) { |
| Log.d(TAG, "onDisconnect(): enter"); |
| } |
| if (V) { |
| logHeader(req); |
| } |
| notifyUpdateWakeLock(); |
| resp.responseCode = ResponseCodes.OBEX_HTTP_OK; |
| if (mCallback != null) { |
| Message msg = Message.obtain(mCallback); |
| msg.what = BluetoothMapService.MSG_SESSION_DISCONNECTED; |
| msg.sendToTarget(); |
| if (V) { |
| Log.v(TAG, "onDisconnect(): msg MSG_SESSION_DISCONNECTED sent out."); |
| } |
| } |
| } |
| |
| @Override |
| public int onAbort(HeaderSet request, HeaderSet reply) { |
| if (D) { |
| Log.d(TAG, "onAbort(): enter."); |
| } |
| notifyUpdateWakeLock(); |
| mIsAborted = true; |
| return ResponseCodes.OBEX_HTTP_OK; |
| } |
| |
| private boolean isUserUnlocked() { |
| UserManager manager = UserManager.get(mContext); |
| return (manager == null || manager.isUserUnlocked()); |
| } |
| |
| @Override |
| public int onPut(final Operation op) { |
| if (D) { |
| Log.d(TAG, "onPut(): enter"); |
| } |
| mIsAborted = false; |
| notifyUpdateWakeLock(); |
| HeaderSet request = null; |
| String type, name; |
| byte[] appParamRaw; |
| BluetoothMapAppParams appParams = null; |
| |
| try { |
| request = op.getReceivedHeader(); |
| if (V) { |
| logHeader(request); |
| } |
| type = (String) request.getHeader(HeaderSet.TYPE); |
| name = (String) request.getHeader(HeaderSet.NAME); |
| appParamRaw = (byte[]) request.getHeader(HeaderSet.APPLICATION_PARAMETER); |
| if (appParamRaw != null) { |
| appParams = new BluetoothMapAppParams(appParamRaw); |
| } |
| if (D) { |
| Log.d(TAG, "type = " + type + ", name = " + name); |
| } |
| if (type.equals(TYPE_MESSAGE_UPDATE)) { |
| if (V) { |
| Log.d(TAG, "TYPE_MESSAGE_UPDATE:"); |
| } |
| return updateInbox(); |
| } else if (type.equals(TYPE_SET_NOTIFICATION_REGISTRATION)) { |
| if (V) { |
| Log.d(TAG, "TYPE_SET_NOTIFICATION_REGISTRATION: NotificationStatus: " |
| + appParams.getNotificationStatus()); |
| } |
| return mObserver.setNotificationRegistration(appParams.getNotificationStatus()); |
| } else if (type.equals(TYPE_SET_NOTIFICATION_FILTER)) { |
| if (V) { |
| Log.d(TAG, "TYPE_SET_NOTIFICATION_FILTER: NotificationFilter: " |
| + appParams.getNotificationFilter()); |
| } |
| if (!isUserUnlocked()) { |
| Log.e(TAG, "Storage locked, " + type + " failed"); |
| return ResponseCodes.OBEX_HTTP_UNAVAILABLE; |
| } |
| mObserver.setNotificationFilter(appParams.getNotificationFilter()); |
| return ResponseCodes.OBEX_HTTP_OK; |
| } else if (type.equals(TYPE_SET_MESSAGE_STATUS)) { |
| if (V) { |
| Log.d(TAG, "TYPE_SET_MESSAGE_STATUS: " + "StatusIndicator: " |
| + appParams.getStatusIndicator() + ", StatusValue: " |
| + appParams.getStatusValue() |
| + ", ExtentedData: "); // TODO: appParams.getExtendedImData()); |
| } |
| if (!isUserUnlocked()) { |
| Log.e(TAG, "Storage locked, " + type + " failed"); |
| return ResponseCodes.OBEX_HTTP_UNAVAILABLE; |
| } |
| return setMessageStatus(name, appParams); |
| } else if (type.equals(TYPE_MESSAGE)) { |
| if (V) { |
| Log.d(TAG, |
| "TYPE_MESSAGE: Transparet: " + appParams.getTransparent() + ", retry: " |
| + appParams.getRetry() + ", charset: " |
| + appParams.getCharset()); |
| } |
| if (!isUserUnlocked()) { |
| Log.e(TAG, "Storage locked, " + type + " failed"); |
| return ResponseCodes.OBEX_HTTP_UNAVAILABLE; |
| } |
| return pushMessage(op, name, appParams, mMessageVersion); |
| } else if (type.equals(TYPE_SET_OWNER_STATUS)) { |
| if (V) { |
| Log.d(TAG, "TYPE_SET_OWNER_STATUS:" + " PresenceAvailability " |
| + appParams.getPresenceAvailability() + ", PresenceStatus: " + appParams |
| .getPresenceStatus() + ", LastActivity: " |
| + appParams.getLastActivityString() + ", ChatStatus: " |
| + appParams.getChatState() + ", ChatStatusConvoId: " |
| + appParams.getChatStateConvoIdString()); |
| } |
| return setOwnerStatus(name, appParams); |
| } |
| |
| } catch (RemoteException e) { |
| //reload the providerClient and return error |
| try { |
| mProviderClient = acquireUnstableContentProviderOrThrow(); |
| } catch (RemoteException e2) { |
| //should not happen |
| } |
| return ResponseCodes.OBEX_HTTP_BAD_REQUEST; |
| } catch (Exception e) { |
| |
| if (D) { |
| Log.e(TAG, "Exception occured while handling request", e); |
| } else { |
| Log.e(TAG, "Exception occured while handling request"); |
| } |
| if (mIsAborted) { |
| return ResponseCodes.OBEX_HTTP_OK; |
| } else { |
| return ResponseCodes.OBEX_HTTP_BAD_REQUEST; |
| } |
| } |
| return ResponseCodes.OBEX_HTTP_BAD_REQUEST; |
| } |
| |
| private int updateInbox() throws RemoteException { |
| if (mAccount != null) { |
| BluetoothMapFolderElement inboxFolder = |
| mCurrentFolder.getFolderByName(BluetoothMapContract.FOLDER_NAME_INBOX); |
| if (inboxFolder != null) { |
| long accountId = mAccountId; |
| if (D) { |
| Log.d(TAG, "updateInbox inbox=" + inboxFolder.getName() + "id=" |
| + inboxFolder.getFolderId()); |
| } |
| |
| final Bundle extras = new Bundle(2); |
| if (accountId != -1) { |
| if (D) { |
| Log.d(TAG, "updateInbox accountId=" + accountId); |
| } |
| extras.putLong(BluetoothMapContract.EXTRA_UPDATE_FOLDER_ID, |
| inboxFolder.getFolderId()); |
| extras.putLong(BluetoothMapContract.EXTRA_UPDATE_ACCOUNT_ID, accountId); |
| } else { |
| // Only error code allowed on an UpdateInbox is OBEX_HTTP_NOT_IMPLEMENTED, |
| // i.e. if e.g. update not allowed on the mailbox |
| if (D) { |
| Log.d(TAG, "updateInbox accountId=0 -> OBEX_HTTP_NOT_IMPLEMENTED"); |
| } |
| return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED; |
| } |
| |
| Uri emailUri = Uri.parse(mBaseUriString); |
| if (D) { |
| Log.d(TAG, "updateInbox in: " + emailUri.toString()); |
| } |
| try { |
| if (D) { |
| Log.d(TAG, "updateInbox call()..."); |
| } |
| Bundle myBundle = |
| mProviderClient.call(BluetoothMapContract.METHOD_UPDATE_FOLDER, null, |
| extras); |
| if (myBundle != null) { |
| return ResponseCodes.OBEX_HTTP_OK; |
| } else { |
| if (D) { |
| Log.d(TAG, "updateInbox call failed"); |
| } |
| return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED; |
| } |
| } catch (RemoteException e) { |
| mProviderClient = acquireUnstableContentProviderOrThrow(); |
| return ResponseCodes.OBEX_HTTP_UNAVAILABLE; |
| } catch (NullPointerException e) { |
| if (D) { |
| Log.e(TAG, "UpdateInbox - if uri or method is null", e); |
| } |
| return ResponseCodes.OBEX_HTTP_UNAVAILABLE; |
| |
| } catch (IllegalArgumentException e) { |
| if (D) { |
| Log.e(TAG, "UpdateInbox - if uri is not known", e); |
| } |
| return ResponseCodes.OBEX_HTTP_UNAVAILABLE; |
| } |
| } |
| } |
| |
| return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED; |
| } |
| |
| private BluetoothMapFolderElement getFolderElementFromName(String folderName) { |
| BluetoothMapFolderElement folderElement = null; |
| |
| if (folderName == null || folderName.trim().isEmpty()) { |
| folderElement = mCurrentFolder; |
| if (D) { |
| Log.d(TAG, "no folder name supplied, setting folder to current: " |
| + folderElement.getName()); |
| } |
| } else { |
| folderElement = mCurrentFolder.getSubFolder(folderName); |
| if (folderElement != null) { |
| if (D) { |
| Log.d(TAG, "Folder name: " + folderName + " resulted in this element: " |
| + folderElement.getName()); |
| } |
| } |
| } |
| return folderElement; |
| } |
| |
| private int pushMessage(final Operation op, String folderName, BluetoothMapAppParams appParams, |
| String messageVersion) { |
| if (appParams.getCharset() == BluetoothMapAppParams.INVALID_VALUE_PARAMETER) { |
| if (D) { |
| Log.d(TAG, "pushMessage: Missing charset - unable to decode message content. " |
| + "appParams.getCharset() = " + appParams.getCharset()); |
| } |
| return ResponseCodes.OBEX_HTTP_PRECON_FAILED; |
| } |
| InputStream bMsgStream = null; |
| try { |
| BluetoothMapFolderElement folderElement = getFolderElementFromName(folderName); |
| if (folderElement == null) { |
| Log.w(TAG, "pushMessage: folderElement == null - sending OBEX_HTTP_PRECON_FAILED"); |
| return ResponseCodes.OBEX_HTTP_PRECON_FAILED; |
| } else { |
| folderName = folderElement.getName(); |
| } |
| if (!folderName.equalsIgnoreCase(BluetoothMapContract.FOLDER_NAME_OUTBOX) && !folderName |
| .equalsIgnoreCase(BluetoothMapContract.FOLDER_NAME_DRAFT)) { |
| if (D) { |
| Log.d(TAG, "pushMessage: Is only allowed to outbox and draft. " + "folderName=" |
| + folderName); |
| } |
| return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE; |
| } |
| |
| /* - Read out the message |
| * - Decode into a bMessage |
| * - send it. |
| */ |
| BluetoothMapbMessage message; |
| bMsgStream = op.openInputStream(); |
| // Decode the messageBody |
| message = BluetoothMapbMessage.parse(bMsgStream, appParams.getCharset()); |
| message.setVersionString(messageVersion); |
| if (D) { |
| Log.d(TAG, "pushMessage: charset" + appParams.getCharset() + "folderId: " |
| + folderElement.getFolderId() + "Name: " + folderName + "TYPE: " |
| + message.getType()); |
| } |
| if (message.getType().equals(TYPE.SMS_GSM) || message.getType().equals(TYPE.SMS_CDMA)) { |
| // Convert messages to the default network type. |
| TelephonyManager tm = |
| (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); |
| if (tm.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM) { |
| message.setType(TYPE.SMS_GSM); |
| } else if (tm.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) { |
| message.setType(TYPE.SMS_CDMA); |
| } |
| if (D) { |
| Log.d(TAG, "Updated message type: " + message.getType()); |
| } |
| } |
| // Send message |
| if (mObserver == null || message == null) { |
| // Should not happen except at shutdown. |
| if (D) { |
| Log.w(TAG, "mObserver or parsed message not available"); |
| } |
| return ResponseCodes.OBEX_HTTP_UNAVAILABLE; |
| } |
| |
| if ((message.getType().equals(TYPE.EMAIL) && (folderElement.getFolderId() == -1)) || ( |
| (message.getType().equals(TYPE.SMS_GSM) || message.getType() |
| .equals(TYPE.SMS_CDMA) || message.getType().equals(TYPE.MMS)) |
| && !folderElement.hasSmsMmsContent())) { |
| if (D) { |
| Log.w(TAG, "Wrong message type recieved"); |
| } |
| return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE; |
| } |
| |
| long handle = mObserver.pushMessage(message, folderElement, appParams, mBaseUriString); |
| if (D) { |
| Log.d(TAG, "pushMessage handle: " + handle); |
| } |
| if (handle < 0) { |
| if (D) { |
| Log.w(TAG, "Message handle not created"); |
| } |
| return ResponseCodes.OBEX_HTTP_UNAVAILABLE; // Should not happen. |
| } |
| HeaderSet replyHeaders = new HeaderSet(); |
| String handleStr = BluetoothMapUtils.getMapHandle(handle, message.getType()); |
| if (D) { |
| Log.d(TAG, "handleStr: " + handleStr + " message.getType(): " + message.getType()); |
| } |
| replyHeaders.setHeader(HeaderSet.NAME, handleStr); |
| op.sendHeaders(replyHeaders); |
| |
| } catch (RemoteException e) { |
| //reload the providerClient and return error |
| try { |
| mProviderClient = acquireUnstableContentProviderOrThrow(); |
| } catch (RemoteException e2) { |
| //should not happen |
| if (D) { |
| Log.w(TAG, "acquireUnstableContentProviderOrThrow FAILED"); |
| } |
| } |
| return ResponseCodes.OBEX_HTTP_BAD_REQUEST; |
| } catch (IllegalArgumentException e) { |
| if (D) { |
| Log.e(TAG, "Wrongly formatted bMessage received", e); |
| } |
| return ResponseCodes.OBEX_HTTP_PRECON_FAILED; |
| } catch (IOException e) { |
| if (D) { |
| Log.e(TAG, "Exception occured: ", e); |
| } |
| if (mIsAborted) { |
| if (D) { |
| Log.d(TAG, "PushMessage Operation Aborted"); |
| } |
| return ResponseCodes.OBEX_HTTP_OK; |
| } else { |
| return ResponseCodes.OBEX_HTTP_BAD_REQUEST; |
| } |
| } catch (Exception e) { |
| if (D) { |
| Log.e(TAG, "Exception:", e); |
| } |
| return ResponseCodes.OBEX_HTTP_BAD_REQUEST; |
| } finally { |
| if (bMsgStream != null) { |
| try { |
| bMsgStream.close(); |
| } catch (IOException e) { |
| } |
| } |
| } |
| return ResponseCodes.OBEX_HTTP_OK; |
| } |
| |
| private int setMessageStatus(String msgHandle, BluetoothMapAppParams appParams) { |
| int indicator = appParams.getStatusIndicator(); |
| int value = appParams.getStatusValue(); |
| String extendedData = ""; // TODO: appParams.getExtendedImData(); |
| |
| long handle; |
| BluetoothMapUtils.TYPE msgType; |
| |
| if (msgHandle == null) { |
| return ResponseCodes.OBEX_HTTP_PRECON_FAILED; |
| } else if ((indicator == BluetoothMapAppParams.INVALID_VALUE_PARAMETER |
| || value == BluetoothMapAppParams.INVALID_VALUE_PARAMETER) |
| && extendedData == null) { |
| return ResponseCodes.OBEX_HTTP_PRECON_FAILED; |
| } |
| if (mObserver == null) { |
| if (D) { |
| Log.e(TAG, "Error: no mObserver!"); |
| } |
| return ResponseCodes.OBEX_HTTP_UNAVAILABLE; // Should not happen. |
| } |
| |
| try { |
| handle = BluetoothMapUtils.getCpHandle(msgHandle); |
| msgType = BluetoothMapUtils.getMsgTypeFromHandle(msgHandle); |
| if (D) { |
| Log.d(TAG, "setMessageStatus. Handle:" + handle + ", MsgType: " + msgType); |
| } |
| } catch (NumberFormatException e) { |
| Log.w(TAG, "Wrongly formatted message handle: " + msgHandle); |
| return ResponseCodes.OBEX_HTTP_PRECON_FAILED; |
| } catch (IllegalArgumentException e) { |
| Log.w(TAG, "Message type not found in handle string: " + msgHandle); |
| return ResponseCodes.OBEX_HTTP_PRECON_FAILED; |
| } |
| |
| if (indicator == BluetoothMapAppParams.STATUS_INDICATOR_DELETED) { |
| if (!mObserver.setMessageStatusDeleted(handle, msgType, mCurrentFolder, mBaseUriString, |
| value)) { |
| if (D) { |
| Log.w(TAG, "setMessageStatusDeleted failed"); |
| } |
| return ResponseCodes.OBEX_HTTP_UNAVAILABLE; |
| } |
| } else if (indicator == BluetoothMapAppParams.STATUS_INDICATOR_READ) { |
| try { |
| if (!mObserver.setMessageStatusRead(handle, msgType, mBaseUriString, value)) { |
| if (D) { |
| Log.w(TAG, "not able to update the message"); |
| } |
| return ResponseCodes.OBEX_HTTP_UNAVAILABLE; |
| } |
| } catch (RemoteException e) { |
| if (D) { |
| Log.w(TAG, "Error in setMessageStatusRead()", e); |
| } |
| return ResponseCodes.OBEX_HTTP_UNAVAILABLE; |
| } |
| } |
| return ResponseCodes.OBEX_HTTP_OK; |
| } |
| |
| private int setOwnerStatus(String msgHandle, BluetoothMapAppParams appParams) |
| throws RemoteException { |
| // This does only work for IM |
| if (mAccount != null && mAccount.getType() == BluetoothMapUtils.TYPE.IM) { |
| final Bundle extras = new Bundle(5); |
| |
| int presenceState = appParams.getPresenceAvailability(); |
| String presenceStatus = appParams.getPresenceStatus(); |
| long lastActivity = appParams.getLastActivity(); |
| int chatState = appParams.getChatState(); |
| String chatStatusConvoId = appParams.getChatStateConvoIdString(); |
| |
| if (presenceState == BluetoothMapAppParams.INVALID_VALUE_PARAMETER |
| && presenceStatus == null |
| && lastActivity == BluetoothMapAppParams.INVALID_VALUE_PARAMETER |
| && chatState == BluetoothMapAppParams.INVALID_VALUE_PARAMETER |
| && chatStatusConvoId == null) { |
| return ResponseCodes.OBEX_HTTP_PRECON_FAILED; |
| } |
| |
| if (presenceState != BluetoothMapAppParams.INVALID_VALUE_PARAMETER) { |
| extras.putInt(BluetoothMapContract.EXTRA_PRESENCE_STATE, presenceState); |
| } |
| if (presenceStatus != null) { |
| extras.putString(BluetoothMapContract.EXTRA_PRESENCE_STATUS, presenceStatus); |
| } |
| if (lastActivity != BluetoothMapAppParams.INVALID_VALUE_PARAMETER) { |
| extras.putLong(BluetoothMapContract.EXTRA_LAST_ACTIVE, lastActivity); |
| } |
| if (chatState != BluetoothMapAppParams.INVALID_VALUE_PARAMETER |
| && chatStatusConvoId != null) { |
| extras.putInt(BluetoothMapContract.EXTRA_CHAT_STATE, chatState); |
| extras.putString(BluetoothMapContract.EXTRA_CONVERSATION_ID, chatStatusConvoId); |
| } |
| |
| Uri uri = Uri.parse(mBaseUriString); |
| if (D) { |
| Log.d(TAG, "setOwnerStatus in: " + uri.toString()); |
| } |
| try { |
| if (D) { |
| Log.d(TAG, "setOwnerStatus call()..."); |
| } |
| Bundle myBundle = |
| mProviderClient.call(BluetoothMapContract.METHOD_SET_OWNER_STATUS, null, |
| extras); |
| if (myBundle != null) { |
| return ResponseCodes.OBEX_HTTP_OK; |
| } else { |
| if (D) { |
| Log.d(TAG, "setOwnerStatus call failed"); |
| } |
| return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED; |
| } |
| } catch (RemoteException e) { |
| mProviderClient = acquireUnstableContentProviderOrThrow(); |
| return ResponseCodes.OBEX_HTTP_UNAVAILABLE; |
| } catch (NullPointerException e) { |
| if (D) { |
| Log.e(TAG, "setOwnerStatus - if uri or method is null", e); |
| } |
| return ResponseCodes.OBEX_HTTP_UNAVAILABLE; |
| } catch (IllegalArgumentException e) { |
| if (D) { |
| Log.e(TAG, "setOwnerStatus - if uri is not known", e); |
| } |
| return ResponseCodes.OBEX_HTTP_UNAVAILABLE; |
| } |
| } |
| return ResponseCodes.OBEX_HTTP_UNAVAILABLE; |
| } |
| |
| @Override |
| public int onSetPath(final HeaderSet request, final HeaderSet reply, final boolean backup, |
| final boolean create) { |
| String folderName; |
| BluetoothMapFolderElement folder; |
| notifyUpdateWakeLock(); |
| try { |
| folderName = (String) request.getHeader(HeaderSet.NAME); |
| } catch (Exception e) { |
| if (D) { |
| Log.e(TAG, "request headers error", e); |
| } else { |
| Log.e(TAG, "request headers error"); |
| } |
| return ResponseCodes.OBEX_HTTP_BAD_REQUEST; |
| } |
| |
| if (V) { |
| logHeader(request); |
| } |
| if (D) { |
| Log.d(TAG, "onSetPath name is " + folderName + " backup: " + backup + " create: " |
| + create); |
| } |
| |
| if (backup) { |
| if (mCurrentFolder.getParent() != null) { |
| mCurrentFolder = mCurrentFolder.getParent(); |
| } else { |
| return ResponseCodes.OBEX_HTTP_BAD_REQUEST; |
| } |
| } |
| |
| if (folderName == null || folderName.trim().isEmpty()) { |
| if (!backup) { |
| mCurrentFolder = mCurrentFolder.getRoot(); |
| } |
| } else { |
| folder = mCurrentFolder.getSubFolder(folderName); |
| if (folder != null) { |
| mCurrentFolder = folder; |
| } else { |
| return ResponseCodes.OBEX_HTTP_BAD_REQUEST; |
| } |
| } |
| if (V) { |
| Log.d(TAG, "Current Folder: " + mCurrentFolder.getName()); |
| } |
| return ResponseCodes.OBEX_HTTP_OK; |
| } |
| |
| @Override |
| public void onClose() { |
| if (mCallback != null) { |
| Message msg = Message.obtain(mCallback); |
| msg.what = BluetoothMapService.MSG_SERVERSESSION_CLOSE; |
| msg.arg1 = mMasId; |
| msg.sendToTarget(); |
| if (D) { |
| Log.d(TAG, "onClose(): msg MSG_SERVERSESSION_CLOSE sent out."); |
| } |
| |
| } |
| if (mProviderClient != null) { |
| mProviderClient.close(); |
| mProviderClient = null; |
| } |
| |
| } |
| |
| @Override |
| public int onGet(Operation op) { |
| notifyUpdateWakeLock(); |
| mIsAborted = false; |
| HeaderSet request; |
| String type; |
| String name; |
| byte[] appParamRaw = null; |
| BluetoothMapAppParams appParams = null; |
| try { |
| request = op.getReceivedHeader(); |
| type = (String) request.getHeader(HeaderSet.TYPE); |
| |
| appParamRaw = (byte[]) request.getHeader(HeaderSet.APPLICATION_PARAMETER); |
| if (appParamRaw != null) { |
| appParams = new BluetoothMapAppParams(appParamRaw); |
| } |
| |
| if (V) { |
| logHeader(request); |
| } |
| if (D) { |
| Log.d(TAG, "OnGet type is " + type); |
| } |
| |
| if (type == null) { |
| if (V) { |
| Log.d(TAG, "type is null?" + type); |
| } |
| return ResponseCodes.OBEX_HTTP_BAD_REQUEST; |
| } |
| |
| if (type.equals(TYPE_GET_FOLDER_LISTING)) { |
| if (V && appParams != null) { |
| Log.d(TAG, |
| "TYPE_GET_FOLDER_LISTING: MaxListCount = " + appParams.getMaxListCount() |
| + ", ListStartOffset = " + appParams.getStartOffset()); |
| } |
| // Block until all packets have been send. |
| return sendFolderListingRsp(op, appParams); |
| } else if (type.equals(TYPE_GET_MESSAGE_LISTING)) { |
| name = (String) request.getHeader(HeaderSet.NAME); |
| if (V && appParams != null) { |
| Log.d(TAG, "TYPE_GET_MESSAGE_LISTING: folder name is: " + name |
| + ", MaxListCount = " + appParams.getMaxListCount() |
| + ", ListStartOffset = " + appParams.getStartOffset()); |
| Log.d(TAG, |
| "SubjectLength = " + appParams.getSubjectLength() + ", ParameterMask = " |
| + appParams.getParameterMask()); |
| Log.d(TAG, "FilterMessageType = " + appParams.getFilterMessageType()); |
| Log.d(TAG, "FilterPeriodBegin = " + appParams.getFilterPeriodBeginString() |
| + ", FilterPeriodEnd = " + appParams.getFilterPeriodEndString() |
| + ", FilterReadStatus = " + appParams.getFilterReadStatus()); |
| Log.d(TAG, "FilterRecipient = " + appParams.getFilterRecipient() |
| + ", FilterOriginator = " + appParams.getFilterOriginator()); |
| Log.d(TAG, "FilterPriority = " + appParams.getFilterPriority()); |
| long tmpLong = appParams.getFilterMsgHandle(); |
| Log.d(TAG, "FilterMsgHandle = " + ( |
| (tmpLong == BluetoothMapAppParams.INVALID_VALUE_PARAMETER) ? "" |
| : Long.toHexString(tmpLong))); |
| SignedLongLong tmpLongLong = appParams.getFilterConvoId(); |
| Log.d(TAG, "FilterConvoId = " + ((tmpLongLong == null) ? "" |
| : Long.toHexString(tmpLongLong.getLeastSignificantBits()))); |
| } |
| if (!isUserUnlocked()) { |
| Log.e(TAG, "Storage locked, " + type + " failed"); |
| return ResponseCodes.OBEX_HTTP_UNAVAILABLE; |
| } |
| // Block until all packets have been send. |
| return sendMessageListingRsp(op, appParams, name); |
| |
| } else if (type.equals(TYPE_GET_CONVO_LISTING)) { |
| name = (String) request.getHeader(HeaderSet.NAME); |
| if (V && appParams != null) { |
| Log.d(TAG, "TYPE_GET_CONVO_LISTING: name is" + name + ", MaxListCount = " |
| + appParams.getMaxListCount() + ", ListStartOffset = " |
| + appParams.getStartOffset()); |
| Log.d(TAG, |
| "FilterLastActivityBegin = " + appParams.getFilterLastActivityBegin()); |
| Log.d(TAG, "FilterLastActivityEnd = " + appParams.getFilterLastActivityEnd()); |
| Log.d(TAG, "FilterReadStatus = " + appParams.getFilterReadStatus()); |
| Log.d(TAG, "FilterRecipient = " + appParams.getFilterRecipient()); |
| } |
| if (!isUserUnlocked()) { |
| Log.e(TAG, "Storage locked, " + type + " failed"); |
| return ResponseCodes.OBEX_HTTP_UNAVAILABLE; |
| } |
| // Block until all packets have been send. |
| return sendConvoListingRsp(op, appParams, name); |
| } else if (type.equals(TYPE_GET_MAS_INSTANCE_INFORMATION)) { |
| if (V && appParams != null) { |
| Log.d(TAG, |
| "TYPE_MESSAGE (GET): MASInstandeId = " + appParams.getMasInstanceId()); |
| } |
| // Block until all packets have been send. |
| return sendMASInstanceInformationRsp(op, appParams); |
| } else if (type.equals(TYPE_MESSAGE)) { |
| name = (String) request.getHeader(HeaderSet.NAME); |
| if (V && appParams != null) { |
| Log.d(TAG, "TYPE_MESSAGE (GET): name is" + name + ", Attachment = " |
| + appParams.getAttachment() + ", Charset = " + appParams.getCharset() |
| + ", FractionRequest = " + appParams.getFractionRequest()); |
| } |
| if (!isUserUnlocked()) { |
| Log.e(TAG, "Storage locked, " + type + " failed"); |
| return ResponseCodes.OBEX_HTTP_UNAVAILABLE; |
| } |
| // Block until all packets have been send. |
| return sendGetMessageRsp(op, name, appParams, mMessageVersion); |
| } else { |
| Log.w(TAG, "unknown type request: " + type); |
| return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE; |
| } |
| |
| } catch (IllegalArgumentException e) { |
| Log.e(TAG, "Exception:", e); |
| return ResponseCodes.OBEX_HTTP_PRECON_FAILED; |
| } catch (ParseException e) { |
| Log.e(TAG, "Exception:", e); |
| return ResponseCodes.OBEX_HTTP_PRECON_FAILED; |
| } catch (Exception e) { |
| if (D) { |
| Log.e(TAG, "Exception occured while handling request", e); |
| } else { |
| Log.e(TAG, "Exception occured while handling request"); |
| } |
| if (mIsAborted) { |
| if (D) { |
| Log.d(TAG, "onGet Operation Aborted"); |
| } |
| return ResponseCodes.OBEX_HTTP_OK; |
| } else { |
| return ResponseCodes.OBEX_HTTP_BAD_REQUEST; |
| } |
| } |
| } |
| |
| /** |
| * Generate and send the message listing response based on an application |
| * parameter header. This function call will block until complete or aborted |
| * by the peer. Fragmentation of packets larger than the obex packet size |
| * will be handled by this function. |
| * |
| * @param op |
| * The OBEX operation. |
| * @param appParams |
| * The application parameter header |
| * @return {@link ResponseCodes.OBEX_HTTP_OK} on success or |
| * {@link ResponseCodes.OBEX_HTTP_BAD_REQUEST} on error. |
| */ |
| private int sendMessageListingRsp(Operation op, BluetoothMapAppParams appParams, |
| String folderName) { |
| OutputStream outStream = null; |
| byte[] outBytes = null; |
| int maxChunkSize, bytesToWrite, bytesWritten = 0, listSize; |
| boolean hasUnread = false; |
| HeaderSet replyHeaders = new HeaderSet(); |
| BluetoothMapAppParams outAppParams = new BluetoothMapAppParams(); |
| BluetoothMapMessageListing outList; |
| if (appParams == null) { |
| appParams = new BluetoothMapAppParams(); |
| appParams.setMaxListCount(1024); |
| appParams.setStartOffset(0); |
| } |
| |
| /* MAP Spec 1.3 introduces the following |
| * Messagehandle filtering: |
| * msgListing (messageHandle=X) -> other allowed filters: parametereMask, subjectMaxLength |
| * ConversationID filtering: |
| * msgListing (convoId empty) -> should work as normal msgListing in valid folders |
| * msgListing (convoId=0, no other filters) -> should return all messages in all folders |
| * msgListing (convoId=N, other filters) -> should return all messages in conversationID=N |
| * according to filters requested |
| */ |
| BluetoothMapFolderElement folderToList = null; |
| if (appParams.getFilterMsgHandle() != BluetoothMapAppParams.INVALID_VALUE_PARAMETER |
| || appParams.getFilterConvoId() != null) { |
| // If messageHandle or convoId filtering ignore folder |
| Log.v(TAG, "sendMessageListingRsp: ignore folder "); |
| folderToList = mCurrentFolder.getRoot(); |
| folderToList.setIngore(true); |
| } else { |
| folderToList = getFolderElementFromName(folderName); |
| if (folderToList == null) { |
| Log.w(TAG, "sendMessageListingRsp: folderToList == " |
| + "null-sending OBEX_HTTP_BAD_REQUEST"); |
| return ResponseCodes.OBEX_HTTP_BAD_REQUEST; |
| } |
| Log.v(TAG, "sendMessageListingRsp: has sms " + folderToList.hasSmsMmsContent() |
| + ", has email " + folderToList.hasEmailContent() + ", has IM " |
| + folderToList.hasImContent()); |
| } |
| |
| try { |
| if (appParams.getMaxListCount() == BluetoothMapAppParams.INVALID_VALUE_PARAMETER) { |
| appParams.setMaxListCount(1024); |
| } |
| |
| if (appParams.getStartOffset() == BluetoothMapAppParams.INVALID_VALUE_PARAMETER) { |
| appParams.setStartOffset(0); |
| } |
| |
| // Check to see if we only need to send the size - hence no need to encode. |
| if (appParams.getMaxListCount() != 0) { |
| outList = mOutContent.msgListing(folderToList, appParams); |
| // Generate the byte stream |
| outAppParams.setMessageListingSize(outList.getCount()); |
| String version; |
| if (0 < (mRemoteFeatureMask |
| & BluetoothMapUtils.MAP_FEATURE_MESSAGE_LISTING_FORMAT_V11_BIT)) { |
| version = BluetoothMapUtils.MAP_V11_STR; |
| } else { |
| version = BluetoothMapUtils.MAP_V10_STR; |
| } |
| /* This will only set the version, the bit must also be checked before adding any |
| * 1.1 bits to the listing. */ |
| outBytes = outList.encode(mThreadIdSupport, version); |
| hasUnread = outList.hasUnread(); |
| } else { |
| listSize = mOutContent.msgListingSize(folderToList, appParams); |
| hasUnread = mOutContent.msgListingHasUnread(folderToList, appParams); |
| outAppParams.setMessageListingSize(listSize); |
| op.noBodyHeader(); |
| } |
| folderToList.setIngore(false); |
| // Build the application parameter header |
| // let the peer know if there are unread messages in the list |
| if (hasUnread) { |
| outAppParams.setNewMessage(1); |
| } else { |
| outAppParams.setNewMessage(0); |
| } |
| if ((mRemoteFeatureMask & BluetoothMapUtils.MAP_FEATURE_DATABASE_INDENTIFIER_BIT) |
| == BluetoothMapUtils.MAP_FEATURE_DATABASE_INDENTIFIER_BIT) { |
| outAppParams.setDatabaseIdentifier(0, mMasInstance.getDbIdentifier()); |
| } |
| if ((mRemoteFeatureMask & BluetoothMapUtils.MAP_FEATURE_FOLDER_VERSION_COUNTER_BIT) |
| == BluetoothMapUtils.MAP_FEATURE_FOLDER_VERSION_COUNTER_BIT) { |
| // Force update of version counter if needed |
| mObserver.refreshFolderVersionCounter(); |
| outAppParams.setFolderVerCounter(mMasInstance.getFolderVersionCounter(), 0); |
| } |
| outAppParams.setMseTime(Calendar.getInstance().getTime().getTime()); |
| replyHeaders.setHeader(HeaderSet.APPLICATION_PARAMETER, outAppParams.encodeParams()); |
| op.sendHeaders(replyHeaders); |
| |
| // Open the OBEX body stream |
| outStream = op.openOutputStream(); |
| } catch (IOException e) { |
| Log.w(TAG, "sendMessageListingRsp: IOException - sending OBEX_HTTP_BAD_REQUEST", e); |
| if (outStream != null) { |
| try { |
| outStream.close(); |
| } catch (IOException ex) { |
| } |
| } |
| if (mIsAborted) { |
| if (D) { |
| Log.d(TAG, "sendMessageListingRsp Operation Aborted"); |
| } |
| return ResponseCodes.OBEX_HTTP_OK; |
| } else { |
| return ResponseCodes.OBEX_HTTP_BAD_REQUEST; |
| } |
| } catch (IllegalArgumentException e) { |
| Log.w(TAG, "sendMessageListingRsp: IllegalArgumentException" |
| + " - sending OBEX_HTTP_BAD_REQUEST", e); |
| if (outStream != null) { |
| try { |
| outStream.close(); |
| } catch (IOException ex) { |
| } |
| } |
| return ResponseCodes.OBEX_HTTP_BAD_REQUEST; |
| } |
| |
| maxChunkSize = op.getMaxPacketSize(); // This must be called after setting the headers. |
| if (outBytes != null) { |
| try { |
| while (bytesWritten < outBytes.length && !mIsAborted) { |
| bytesToWrite = Math.min(maxChunkSize, outBytes.length - bytesWritten); |
| outStream.write(outBytes, bytesWritten, bytesToWrite); |
| bytesWritten += bytesToWrite; |
| } |
| } catch (IOException e) { |
| if (D) { |
| Log.w(TAG, e); |
| } |
| // We were probably aborted or disconnected |
| } finally { |
| if (outStream != null) { |
| try { |
| outStream.close(); |
| } catch (IOException e) { |
| } |
| } |
| } |
| if (bytesWritten != outBytes.length && !mIsAborted) { |
| Log.w(TAG, "sendMessageListingRsp: bytesWritten != outBytes.length" |
| + " - sending OBEX_HTTP_BAD_REQUEST"); |
| return ResponseCodes.OBEX_HTTP_BAD_REQUEST; |
| } |
| } else { |
| if (outStream != null) { |
| try { |
| outStream.close(); |
| } catch (IOException e) { |
| } |
| } |
| } |
| return ResponseCodes.OBEX_HTTP_OK; |
| } |
| |
| /** |
| * Update the {@link BluetoothMapAppParams} object message type filter mask to only contain |
| * message types supported by this mas instance. |
| * Could the folder be used in stead? |
| * @param appParams Reference to the object to update |
| * @param overwrite True: The msgType will be overwritten to match the message types supported |
| * by this MAS instance. False: any unsupported message types will be masked out. |
| */ |
| private void setMsgTypeFilterParams(BluetoothMapAppParams appParams, boolean overwrite) { |
| int masFilterMask = 0; |
| if (!mEnableSmsMms) { |
| masFilterMask |= BluetoothMapAppParams.FILTER_NO_SMS_CDMA; |
| masFilterMask |= BluetoothMapAppParams.FILTER_NO_SMS_GSM; |
| masFilterMask |= BluetoothMapAppParams.FILTER_NO_MMS; |
| } |
| if (mAccount == null) { |
| masFilterMask |= BluetoothMapAppParams.FILTER_NO_EMAIL; |
| masFilterMask |= BluetoothMapAppParams.FILTER_NO_IM; |
| } else { |
| if (!(mAccount.getType() == BluetoothMapUtils.TYPE.EMAIL)) { |
| masFilterMask |= BluetoothMapAppParams.FILTER_NO_EMAIL; |
| } |
| if (!(mAccount.getType() == BluetoothMapUtils.TYPE.IM)) { |
| masFilterMask |= BluetoothMapAppParams.FILTER_NO_IM; |
| } |
| } |
| if (overwrite) { |
| appParams.setFilterMessageType(masFilterMask); |
| } else { |
| int newMask = appParams.getFilterMessageType(); |
| if (newMask == BluetoothMapAppParams.INVALID_VALUE_PARAMETER) { |
| appParams.setFilterMessageType(newMask); |
| } else { |
| newMask |= masFilterMask; |
| appParams.setFilterMessageType(newMask); |
| } |
| } |
| } |
| |
| /** |
| * Generate and send the Conversation listing response based on an application |
| * parameter header. This function call will block until complete or aborted |
| * by the peer. Fragmentation of packets larger than the obex packet size |
| * will be handled by this function. |
| * |
| * @param op |
| * The OBEX operation. |
| * @param appParams |
| * The application parameter header |
| * @return {@link ResponseCodes.OBEX_HTTP_OK} on success or |
| * {@link ResponseCodes.OBEX_HTTP_BAD_REQUEST} on error. |
| */ |
| private int sendConvoListingRsp(Operation op, BluetoothMapAppParams appParams, |
| String folderName) { |
| OutputStream outStream = null; |
| byte[] outBytes = null; |
| int maxChunkSize, bytesToWrite, bytesWritten = 0; |
| //boolean hasUnread = false; |
| HeaderSet replyHeaders = new HeaderSet(); |
| BluetoothMapAppParams outAppParams = new BluetoothMapAppParams(); |
| BluetoothMapConvoListing outList; |
| if (appParams == null) { |
| appParams = new BluetoothMapAppParams(); |
| appParams.setMaxListCount(1024); |
| appParams.setStartOffset(0); |
| } |
| // As the app parameters do not carry which message types to list, we set the filter here |
| // to all message types supported by this instance. |
| setMsgTypeFilterParams(appParams, true); |
| |
| // Check to see if we only need to send the size - hence no need to encode. |
| try { |
| if (appParams.getMaxListCount() == BluetoothMapAppParams.INVALID_VALUE_PARAMETER) { |
| appParams.setMaxListCount(1024); |
| } |
| |
| if (appParams.getStartOffset() == BluetoothMapAppParams.INVALID_VALUE_PARAMETER) { |
| appParams.setStartOffset(0); |
| } |
| |
| if (appParams.getMaxListCount() != 0) { |
| outList = mOutContent.convoListing(appParams, false); |
| outAppParams.setConvoListingSize(outList.getCount()); |
| // Generate the byte stream |
| outBytes = outList.encode(); // Include thread ID for clients that supports it. |
| if (D) { |
| Log.d(TAG, "outBytes size:" + outBytes.length); |
| } |
| } else { |
| outList = mOutContent.convoListing(appParams, true); |
| outAppParams.setConvoListingSize(outList.getCount()); |
| if (mEnableSmsMms) { |
| mOutContent.refreshSmsMmsConvoVersions(); |
| } |
| if (mAccount != null) { |
| mOutContent.refreshImEmailConvoVersions(); |
| } |
| // Force update of version counter if needed |
| mObserver.refreshConvoListVersionCounter(); |
| if (0 < (mRemoteFeatureMask |
| & BluetoothMapUtils.MAP_FEATURE_CONVERSATION_VERSION_COUNTER_BIT)) { |
| outAppParams.setConvoListingVerCounter( |
| mMasInstance.getCombinedConvoListVersionCounter(), 0); |
| } |
| op.noBodyHeader(); |
| } |
| if (D) { |
| Log.d(TAG, "outList size:" + outList.getCount() + " MaxListCount: " |
| + appParams.getMaxListCount()); |
| } |
| outList = null; // We don't need it anymore - we might as well give it up for GC |
| outAppParams.setDatabaseIdentifier(0, mMasInstance.getDbIdentifier()); |
| |
| // Build the application parameter header |
| // The MseTime is not in the CR - but I think it is missing. |
| outAppParams.setMseTime(Calendar.getInstance().getTime().getTime()); |
| replyHeaders.setHeader(HeaderSet.APPLICATION_PARAMETER, outAppParams.encodeParams()); |
| op.sendHeaders(replyHeaders); |
| |
| // Open the OBEX body stream |
| outStream = op.openOutputStream(); |
| } catch (IOException e) { |
| Log.w(TAG, "sendConvoListingRsp: IOException - sending OBEX_HTTP_BAD_REQUEST", e); |
| if (outStream != null) { |
| try { |
| outStream.close(); |
| } catch (IOException ex) { |
| } |
| } |
| if (mIsAborted) { |
| if (D) { |
| Log.d(TAG, "sendConvoListingRsp Operation Aborted"); |
| } |
| return ResponseCodes.OBEX_HTTP_OK; |
| } else { |
| return ResponseCodes.OBEX_HTTP_BAD_REQUEST; |
| } |
| } catch (IllegalArgumentException e) { |
| Log.w(TAG, "sendConvoListingRsp: IllegalArgumentException" |
| + " - sending OBEX_HTTP_BAD_REQUEST", e); |
| if (outStream != null) { |
| try { |
| outStream.close(); |
| } catch (IOException ex) { |
| } |
| } |
| return ResponseCodes.OBEX_HTTP_BAD_REQUEST; |
| } |
| |
| maxChunkSize = op.getMaxPacketSize(); // This must be called after setting the headers. |
| if (outBytes != null) { |
| try { |
| while (bytesWritten < outBytes.length && !mIsAborted) { |
| bytesToWrite = Math.min(maxChunkSize, outBytes.length - bytesWritten); |
| outStream.write(outBytes, bytesWritten, bytesToWrite); |
| bytesWritten += bytesToWrite; |
| } |
| } catch (IOException e) { |
| if (D) { |
| Log.w(TAG, e); |
| } |
| // We were probably aborted or disconnected |
| } finally { |
| if (outStream != null) { |
| try { |
| outStream.close(); |
| } catch (IOException e) { |
| } |
| } |
| } |
| if (bytesWritten != outBytes.length && !mIsAborted) { |
| Log.w(TAG, "sendConvoListingRsp: bytesWritten != outBytes.length" |
| + " - sending OBEX_HTTP_BAD_REQUEST"); |
| return ResponseCodes.OBEX_HTTP_BAD_REQUEST; |
| } |
| } else { |
| if (outStream != null) { |
| try { |
| outStream.close(); |
| } catch (IOException e) { |
| } |
| } |
| } |
| return ResponseCodes.OBEX_HTTP_OK; |
| } |
| |
| /** |
| * Generate and send the Folder listing response based on an application |
| * parameter header. This function call will block until complete or aborted |
| * by the peer. Fragmentation of packets larger than the obex packet size |
| * will be handled by this function. |
| * |
| * @param op |
| * The OBEX operation. |
| * @param appParams |
| * The application parameter header |
| * @return {@link ResponseCodes.OBEX_HTTP_OK} on success or |
| * {@link ResponseCodes.OBEX_HTTP_BAD_REQUEST} on error. |
| */ |
| private int sendFolderListingRsp(Operation op, BluetoothMapAppParams appParams) { |
| OutputStream outStream = null; |
| byte[] outBytes = null; |
| BluetoothMapAppParams outAppParams = new BluetoothMapAppParams(); |
| int maxChunkSize, bytesWritten = 0; |
| HeaderSet replyHeaders = new HeaderSet(); |
| int bytesToWrite, maxListCount, listStartOffset; |
| if (appParams == null) { |
| appParams = new BluetoothMapAppParams(); |
| appParams.setMaxListCount(1024); |
| } |
| |
| if (V) { |
| Log.v(TAG, "sendFolderList for " + mCurrentFolder.getName()); |
| } |
| |
| try { |
| maxListCount = appParams.getMaxListCount(); |
| listStartOffset = appParams.getStartOffset(); |
| |
| if (listStartOffset == BluetoothMapAppParams.INVALID_VALUE_PARAMETER) { |
| listStartOffset = 0; |
| } |
| |
| if (maxListCount == BluetoothMapAppParams.INVALID_VALUE_PARAMETER) { |
| maxListCount = 1024; |
| } |
| |
| if (maxListCount != 0) { |
| outBytes = mCurrentFolder.encode(listStartOffset, maxListCount); |
| } else { |
| // ESR08 specified that this shall only be included for MaxListCount=0 |
| outAppParams.setFolderListingSize(mCurrentFolder.getSubFolderCount()); |
| op.noBodyHeader(); |
| } |
| |
| // Build and set the application parameter header |
| replyHeaders.setHeader(HeaderSet.APPLICATION_PARAMETER, outAppParams.encodeParams()); |
| op.sendHeaders(replyHeaders); |
| |
| if (maxListCount != 0) { |
| outStream = op.openOutputStream(); |
| } |
| } catch (IOException e1) { |
| Log.w(TAG, "sendFolderListingRsp: IOException" |
| + " - sending OBEX_HTTP_BAD_REQUEST Exception:", e1); |
| if (outStream != null) { |
| try { |
| outStream.close(); |
| } catch (IOException e) { |
| } |
| } |
| if (mIsAborted) { |
| if (D) { |
| Log.d(TAG, "sendFolderListingRsp Operation Aborted"); |
| } |
| return ResponseCodes.OBEX_HTTP_OK; |
| } else { |
| return ResponseCodes.OBEX_HTTP_BAD_REQUEST; |
| } |
| } catch (IllegalArgumentException e1) { |
| Log.w(TAG, "sendFolderListingRsp: IllegalArgumentException" |
| + " - sending OBEX_HTTP_BAD_REQUEST Exception:", e1); |
| if (outStream != null) { |
| try { |
| outStream.close(); |
| } catch (IOException e) { |
| } |
| } |
| return ResponseCodes.OBEX_HTTP_PRECON_FAILED; |
| } |
| |
| maxChunkSize = op.getMaxPacketSize(); // This must be called after setting the headers. |
| |
| if (outBytes != null) { |
| try { |
| while (bytesWritten < outBytes.length && !mIsAborted) { |
| bytesToWrite = Math.min(maxChunkSize, outBytes.length - bytesWritten); |
| outStream.write(outBytes, bytesWritten, bytesToWrite); |
| bytesWritten += bytesToWrite; |
| } |
| } catch (IOException e) { |
| // We were probably aborted or disconnected |
| } finally { |
| if (outStream != null) { |
| try { |
| outStream.close(); |
| } catch (IOException e) { |
| } |
| } |
| } |
| if (V) { |
| Log.v(TAG, |
| "sendFolderList sent " + bytesWritten + " bytes out of " + outBytes.length); |
| } |
| if (bytesWritten == outBytes.length || mIsAborted) { |
| return ResponseCodes.OBEX_HTTP_OK; |
| } else { |
| return ResponseCodes.OBEX_HTTP_BAD_REQUEST; |
| } |
| } |
| |
| return ResponseCodes.OBEX_HTTP_OK; |
| } |
| |
| /** |
| * Generate and send the get MAS Instance Information response based on an MAS Instance |
| * |
| * @param op |
| * The OBEX operation. |
| * @param appParams |
| * The application parameter header |
| * @return {@link ResponseCodes.OBEX_HTTP_OK} on success or |
| * {@link ResponseCodes.OBEX_HTTP_BAD_REQUEST} on error. |
| */ |
| private int sendMASInstanceInformationRsp(Operation op, BluetoothMapAppParams appParams) { |
| |
| OutputStream outStream = null; |
| byte[] outBytes = null; |
| String outString = null; |
| int maxChunkSize, bytesToWrite, bytesWritten = 0; |
| |
| try { |
| if (mMasId == appParams.getMasInstanceId()) { |
| if (mAccount != null) { |
| if (mAccount.getType() == TYPE.EMAIL) { |
| outString = (mAccount.getName() != null) ? mAccount.getName() |
| : BluetoothMapMasInstance.TYPE_EMAIL_STR; |
| } else if (mAccount.getType() == TYPE.IM) { |
| outString = mAccount.getUciFull(); |
| if (outString == null) { |
| String uci = mAccount.getUci(); |
| // TODO: Do we need to align this with HF/PBAP |
| StringBuilder sb = |
| new StringBuilder(uci == null ? 5 : 5 + uci.length()); |
| sb.append("un"); |
| if (mMasId < 10) { |
| sb.append("00"); |
| } else if (mMasId < 100) { |
| sb.append("0"); |
| } |
| sb.append(mMasId); |
| if (uci != null) { |
| sb.append(":").append(uci); |
| } |
| outString = sb.toString(); |
| } |
| } |
| } else { |
| outString = BluetoothMapMasInstance.TYPE_SMS_MMS_STR; |
| // TODO: Add phone number if possible |
| } |
| } else { |
| return ResponseCodes.OBEX_HTTP_BAD_REQUEST; |
| } |
| |
| /* Ensure byte array max length is 200 containing valid UTF-8 characters */ |
| outBytes = BluetoothMapUtils.truncateUtf8StringToBytearray(outString, |
| MAS_INSTANCE_INFORMATION_LENGTH); |
| |
| // Open the OBEX body stream |
| outStream = op.openOutputStream(); |
| |
| } catch (IOException e) { |
| Log.w(TAG, "sendMASInstanceInformationRsp: IOException" |
| + " - sending OBEX_HTTP_BAD_REQUEST", e); |
| if (mIsAborted) { |
| if (D) { |
| Log.d(TAG, "sendMASInstanceInformationRsp Operation Aborted"); |
| } |
| return ResponseCodes.OBEX_HTTP_OK; |
| } else { |
| return ResponseCodes.OBEX_HTTP_BAD_REQUEST; |
| } |
| } |
| |
| maxChunkSize = op.getMaxPacketSize(); // This must be called after setting the headers. |
| |
| if (outBytes != null) { |
| try { |
| while (bytesWritten < outBytes.length && !mIsAborted) { |
| bytesToWrite = Math.min(maxChunkSize, outBytes.length - bytesWritten); |
| outStream.write(outBytes, bytesWritten, bytesToWrite); |
| bytesWritten += bytesToWrite; |
| } |
| } catch (IOException e) { |
| // We were probably aborted or disconnected |
| } finally { |
| if (outStream != null) { |
| try { |
| outStream.close(); |
| } catch (IOException e) { |
| } |
| } |
| } |
| if (V) { |
| Log.v(TAG, "sendMASInstanceInformationRsp sent " + bytesWritten + " bytes out of " |
| + outBytes.length); |
| } |
| if (bytesWritten == outBytes.length || mIsAborted) { |
| return ResponseCodes.OBEX_HTTP_OK; |
| } else { |
| return ResponseCodes.OBEX_HTTP_BAD_REQUEST; |
| } |
| } |
| return ResponseCodes.OBEX_HTTP_OK; |
| } |
| |
| /** |
| * Generate and send the get message response based on an application |
| * parameter header and a handle. |
| * |
| * @param op |
| * The OBEX operation. |
| * @param handle |
| * The handle of the requested message |
| * @param appParams |
| * The application parameter header |
| * @param version |
| * The string representation of the version number(i.e. "1.0") |
| * @return {@link ResponseCodes.OBEX_HTTP_OK} on success or |
| * {@link ResponseCodes.OBEX_HTTP_BAD_REQUEST} on error. |
| */ |
| private int sendGetMessageRsp(Operation op, String handle, BluetoothMapAppParams appParams, |
| String version) { |
| OutputStream outStream = null; |
| byte[] outBytes = null; |
| int maxChunkSize, bytesToWrite, bytesWritten = 0; |
| |
| try { |
| outBytes = mOutContent.getMessage(handle, appParams, mCurrentFolder, version); |
| |
| // If it is a fraction request of Email message, set header before responding |
| if ((BluetoothMapUtils.getMsgTypeFromHandle(handle).equals(TYPE.EMAIL) |
| || (BluetoothMapUtils.getMsgTypeFromHandle(handle).equals(TYPE.IM))) && ( |
| appParams.getFractionRequest() |
| == BluetoothMapAppParams.FRACTION_REQUEST_FIRST)) { |
| BluetoothMapAppParams outAppParams = new BluetoothMapAppParams(); |
| HeaderSet replyHeaders = new HeaderSet(); |
| outAppParams.setFractionDeliver(BluetoothMapAppParams.FRACTION_DELIVER_LAST); |
| // Build and set the application parameter header |
| replyHeaders.setHeader(HeaderSet.APPLICATION_PARAMETER, |
| outAppParams.encodeParams()); |
| op.sendHeaders(replyHeaders); |
| if (V) { |
| Log.v(TAG, "sendGetMessageRsp fractionRequest - " |
| + "set FRACTION_DELIVER_LAST header"); |
| } |
| } |
| outStream = op.openOutputStream(); |
| |
| } catch (IOException e) { |
| Log.w(TAG, "sendGetMessageRsp: IOException - sending OBEX_HTTP_BAD_REQUEST", e); |
| if (outStream != null) { |
| try { |
| outStream.close(); |
| } catch (IOException ex) { |
| } |
| } |
| if (mIsAborted) { |
| if (D) { |
| Log.d(TAG, "sendGetMessageRsp Operation Aborted"); |
| } |
| return ResponseCodes.OBEX_HTTP_OK; |
| } else { |
| return ResponseCodes.OBEX_HTTP_BAD_REQUEST; |
| } |
| } catch (IllegalArgumentException e) { |
| Log.w(TAG, "sendGetMessageRsp: IllegalArgumentException (e.g. invalid handle) - " |
| + "sending OBEX_HTTP_BAD_REQUEST", e); |
| if (outStream != null) { |
| try { |
| outStream.close(); |
| } catch (IOException ex) { |
| } |
| } |
| return ResponseCodes.OBEX_HTTP_BAD_REQUEST; |
| } |
| |
| maxChunkSize = op.getMaxPacketSize(); // This must be called after setting the headers. |
| |
| if (outBytes != null) { |
| try { |
| while (bytesWritten < outBytes.length && !mIsAborted) { |
| bytesToWrite = Math.min(maxChunkSize, outBytes.length - bytesWritten); |
| outStream.write(outBytes, bytesWritten, bytesToWrite); |
| bytesWritten += bytesToWrite; |
| } |
| } catch (IOException e) { |
| // We were probably aborted or disconnected |
| if (D && e.getMessage().equals("Abort Received")) { |
| Log.w(TAG, "getMessage() Aborted...", e); |
| } |
| } finally { |
| if (outStream != null) { |
| try { |
| outStream.close(); |
| } catch (IOException e) { |
| } |
| } |
| } |
| if (bytesWritten == outBytes.length || mIsAborted) { |
| return ResponseCodes.OBEX_HTTP_OK; |
| } else { |
| return ResponseCodes.OBEX_HTTP_BAD_REQUEST; |
| } |
| } |
| |
| return ResponseCodes.OBEX_HTTP_OK; |
| } |
| |
| @Override |
| public int onDelete(HeaderSet request, HeaderSet reply) { |
| if (D) { |
| Log.v(TAG, "onDelete() " + request.toString()); |
| } |
| mIsAborted = false; |
| notifyUpdateWakeLock(); |
| String type, name; |
| byte[] appParamRaw; |
| BluetoothMapAppParams appParams = null; |
| |
| /* TODO: If this is to be placed here, we need to cleanup - e.g. the exception handling */ |
| try { |
| type = (String) request.getHeader(HeaderSet.TYPE); |
| |
| name = (String) request.getHeader(HeaderSet.NAME); |
| appParamRaw = (byte[]) request.getHeader(HeaderSet.APPLICATION_PARAMETER); |
| if (appParamRaw != null) { |
| appParams = new BluetoothMapAppParams(appParamRaw); |
| } |
| if (D) { |
| Log.d(TAG, "type = " + type + ", name = " + name); |
| } |
| if (type.equals(TYPE_SET_NOTIFICATION_FILTER)) { |
| if (V) { |
| Log.d(TAG, "TYPE_SET_NOTIFICATION_FILTER: NotificationFilter: " |
| + appParams.getNotificationFilter()); |
| } |
| mObserver.setNotificationFilter(appParams.getNotificationFilter()); |
| return ResponseCodes.OBEX_HTTP_OK; |
| } else if (type.equals(TYPE_SET_OWNER_STATUS)) { |
| if (V) { |
| Log.d(TAG, "TYPE_SET_OWNER_STATUS:" + " PresenceAvailability " |
| + appParams.getPresenceAvailability() + ", PresenceStatus: " + appParams |
| .getPresenceStatus() + ", LastActivity: " |
| + appParams.getLastActivityString() + ", ChatStatus: " |
| + appParams.getChatState() + ", ChatStatusConvoId: " |
| + appParams.getChatStateConvoIdString()); |
| } |
| return setOwnerStatus(name, appParams); |
| } |
| |
| } catch (RemoteException e) { |
| //reload the providerClient and return error |
| try { |
| mProviderClient = acquireUnstableContentProviderOrThrow(); |
| } catch (RemoteException e2) { |
| //should not happen |
| } |
| return ResponseCodes.OBEX_HTTP_BAD_REQUEST; |
| } catch (Exception e) { |
| |
| if (D) { |
| Log.e(TAG, "Exception occured while handling request", e); |
| } else { |
| Log.e(TAG, "Exception occured while handling request"); |
| } |
| if (mIsAborted) { |
| return ResponseCodes.OBEX_HTTP_OK; |
| } else { |
| return ResponseCodes.OBEX_HTTP_BAD_REQUEST; |
| } |
| } |
| return ResponseCodes.OBEX_HTTP_BAD_REQUEST; |
| } |
| |
| private void notifyUpdateWakeLock() { |
| if (mCallback != null) { |
| Message msg = Message.obtain(mCallback); |
| msg.what = BluetoothMapService.MSG_ACQUIRE_WAKE_LOCK; |
| msg.sendToTarget(); |
| } |
| } |
| |
| private static void logHeader(HeaderSet hs) { |
| Log.v(TAG, "Dumping HeaderSet " + hs.toString()); |
| try { |
| Log.v(TAG, "CONNECTION_ID : " + hs.getHeader(HeaderSet.CONNECTION_ID)); |
| Log.v(TAG, "NAME : " + hs.getHeader(HeaderSet.NAME)); |
| Log.v(TAG, "TYPE : " + hs.getHeader(HeaderSet.TYPE)); |
| Log.v(TAG, "TARGET : " + hs.getHeader(HeaderSet.TARGET)); |
| Log.v(TAG, "WHO : " + hs.getHeader(HeaderSet.WHO)); |
| Log.v(TAG, "APPLICATION_PARAMETER : " + hs.getHeader(HeaderSet.APPLICATION_PARAMETER)); |
| } catch (IOException e) { |
| Log.e(TAG, "dump HeaderSet error " + e); |
| } |
| Log.v(TAG, "NEW!!! Dumping HeaderSet END"); |
| } |
| } |