BluetoothMapObexServer.java revision 72014e2b0523c72ba34ca43d91bc4e8be104d95a
1/*
2* Copyright (C) 2015 Samsung System LSI
3* Licensed under the Apache License, Version 2.0 (the "License");
4* you may not use this file except in compliance with the License.
5* You may obtain a copy of the License at
6*
7*      http://www.apache.org/licenses/LICENSE-2.0
8*
9* Unless required by applicable law or agreed to in writing, software
10* distributed under the License is distributed on an "AS IS" BASIS,
11* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12* See the License for the specific language governing permissions and
13* limitations under the License.
14*/
15package com.android.bluetooth.map;
16
17import android.content.ContentProviderClient;
18import android.content.ContentResolver;
19import android.content.Context;
20import android.database.Cursor;
21import android.net.Uri;
22import android.os.Bundle;
23import android.os.Handler;
24import android.os.Message;
25import android.os.ParcelUuid;
26import android.os.RemoteException;
27import android.text.format.DateUtils;
28import android.util.Log;
29
30import com.android.bluetooth.SignedLongLong;
31import com.android.bluetooth.map.BluetoothMapUtils.TYPE;
32import com.android.bluetooth.map.BluetoothMapMasInstance;
33import com.android.bluetooth.mapapi.BluetoothMapContract;
34
35import java.io.IOException;
36import java.io.InputStream;
37import java.io.OutputStream;
38import java.text.ParseException;
39import java.util.Arrays;
40import java.util.Calendar;
41
42import javax.obex.HeaderSet;
43import javax.obex.Operation;
44import javax.obex.ResponseCodes;
45import javax.obex.ServerRequestHandler;
46
47
48public class BluetoothMapObexServer extends ServerRequestHandler {
49
50    private static final String TAG = "BluetoothMapObexServer";
51
52    private static final boolean D = BluetoothMapService.DEBUG;
53    private static final boolean V = BluetoothMapService.VERBOSE;
54
55    private static final int UUID_LENGTH = 16;
56
57    private static final long PROVIDER_ANR_TIMEOUT = 20 * DateUtils.SECOND_IN_MILLIS;
58
59    /* OBEX header and value used to detect clients that support threadId in the message listing. */
60    private static final int THREADED_MAIL_HEADER_ID = 0xFA;
61    private static final long THREAD_MAIL_KEY = 0x534c5349;
62
63    // 128 bit UUID for MAP
64    private static final byte[] MAP_TARGET = new byte[] {
65             (byte)0xBB, (byte)0x58, (byte)0x2B, (byte)0x40,
66             (byte)0x42, (byte)0x0C, (byte)0x11, (byte)0xDB,
67             (byte)0xB0, (byte)0xDE, (byte)0x08, (byte)0x00,
68             (byte)0x20, (byte)0x0C, (byte)0x9A, (byte)0x66
69             };
70    public static final ParcelUuid MAP =
71            ParcelUuid.fromString("00001134-0000-1000-8000-00805F9B34FB");
72    public static final ParcelUuid MNS =
73            ParcelUuid.fromString("00001133-0000-1000-8000-00805F9B34FB");
74    public static final ParcelUuid MAS =
75            ParcelUuid.fromString("00001132-0000-1000-8000-00805F9B34FB");
76    /* Message types */
77    private static final String TYPE_GET_FOLDER_LISTING              = "x-obex/folder-listing";
78    private static final String TYPE_GET_MESSAGE_LISTING             = "x-bt/MAP-msg-listing";
79    private static final String TYPE_GET_CONVO_LISTING               = "x-bt/MAP-convo-listing";
80    private static final String TYPE_MESSAGE                         = "x-bt/message";
81    private static final String TYPE_SET_MESSAGE_STATUS              = "x-bt/messageStatus";
82    private static final String TYPE_SET_NOTIFICATION_REGISTRATION
83            = "x-bt/MAP-NotificationRegistration";
84    private static final String TYPE_MESSAGE_UPDATE                  = "x-bt/MAP-messageUpdate";
85    private static final String TYPE_GET_MAS_INSTANCE_INFORMATION
86            = "x-bt/MASInstanceInformation";
87    private static final String TYPE_SET_OWNER_STATUS                = "x-bt/participant";
88    private static final String TYPE_SET_NOTIFICATION_FILTER
89            = "x-bt/MAP-notification-filter";
90
91    private static final int MAS_INSTANCE_INFORMATION_LENGTH = 200;
92
93    private BluetoothMapFolderElement mCurrentFolder;
94    private BluetoothMapContentObserver mObserver = null;
95    private Handler mCallback = null;
96    private Context mContext;
97    private boolean mIsAborted = false;
98    BluetoothMapContent mOutContent;
99    private String mBaseUriString = null;
100    private long mAccountId = 0;
101    private BluetoothMapAccountItem mAccount = null;
102    private Uri mEmailFolderUri = null;
103    private int mMasId = 0;
104    private BluetoothMapMasInstance mMasInstance; // TODO: change to interface?
105    // updated during connect if remote has alternative value
106    private int mRemoteFeatureMask = BluetoothMapUtils.MAP_FEATURE_DEFAULT_BITMASK;
107    private boolean mEnableSmsMms = false;
108    private boolean mThreadIdSupport = false; // true if peer supports threadId in msg listing
109    // Defaults message version is 1.0 but 1.1+ if feature bit is set
110    private String mMessageVersion = BluetoothMapUtils.MAP_V10_STR;
111    private String mAuthority;
112    private ContentResolver mResolver;
113    private ContentProviderClient mProviderClient = null;
114
115    public BluetoothMapObexServer(Handler callback,
116                                  Context context,
117                                  BluetoothMapContentObserver observer,
118                                  BluetoothMapMasInstance mas,
119                                  BluetoothMapAccountItem account,
120                                  boolean enableSmsMms) throws RemoteException {
121        super();
122        mCallback = callback;
123        mContext = context;
124        mObserver = observer;
125        mEnableSmsMms = enableSmsMms;
126        mAccount = account;
127        mMasId = mas.getMasId();
128        mMasInstance = mas;
129        mRemoteFeatureMask = mMasInstance.getRemoteFeatureMask();
130
131        if(account != null && account.getProviderAuthority() != null) {
132            mAccountId = account.getAccountId();
133            mAuthority = account.getProviderAuthority();
134            mResolver = mContext.getContentResolver();
135            if (D) Log.d(TAG, "BluetoothMapObexServer(): accountId=" + mAccountId);
136            mBaseUriString = account.mBase_uri + "/";
137            if (D) Log.d(TAG, "BluetoothMapObexServer(): baseUri=" + mBaseUriString);
138            if (account.getType() == TYPE.EMAIL) {
139                mEmailFolderUri = BluetoothMapContract.buildFolderUri(mAuthority,
140                                                                 Long.toString(mAccountId));
141                if (D) Log.d(TAG, "BluetoothMapObexServer(): mEmailFolderUri=" + mEmailFolderUri);
142            }
143            mProviderClient = acquireUnstableContentProviderOrThrow();
144        }
145
146        buildFolderStructure(); /* Build the default folder structure, and set
147                                   mCurrentFolder to root folder */
148        mObserver.setFolderStructure(mCurrentFolder.getRoot());
149
150        mOutContent = new BluetoothMapContent(mContext, mAccount, mMasInstance);
151
152    }
153
154    /**
155     *
156     */
157    private ContentProviderClient acquireUnstableContentProviderOrThrow() throws RemoteException{
158        ContentProviderClient providerClient =
159                            mResolver.acquireUnstableContentProviderClient(mAuthority);
160        if (providerClient == null) {
161            throw new RemoteException("Failed to acquire provider for " + mAuthority);
162        }
163        providerClient.setDetectNotResponding(PROVIDER_ANR_TIMEOUT);
164        return providerClient;
165    }
166
167    /**
168     * Build the default minimal folder structure, as defined in the MAP specification.
169     */
170    private void buildFolderStructure() throws RemoteException{
171        mCurrentFolder = new BluetoothMapFolderElement("root", null);//This will be the root element
172        mCurrentFolder.setHasSmsMmsContent(mEnableSmsMms);
173        boolean hasIM = false;
174        boolean hasEmail = false;
175        if (mAccount != null) {
176           if (mAccount.getType() == TYPE.IM)
177               hasIM = true;
178           if( mAccount.getType() == TYPE.EMAIL)
179               hasEmail = true;
180        }
181        mCurrentFolder.setHasImContent(hasIM);
182        mCurrentFolder.setHasEmailContent(hasEmail);
183
184        BluetoothMapFolderElement tmpFolder;
185        tmpFolder = mCurrentFolder.addFolder("telecom"); // root/telecom
186        tmpFolder.setHasSmsMmsContent(mEnableSmsMms);
187        tmpFolder.setHasImContent(hasIM);
188        tmpFolder.setHasEmailContent(hasEmail);
189
190        tmpFolder = tmpFolder.addFolder("msg");          // root/telecom/msg
191        tmpFolder.setHasSmsMmsContent(mEnableSmsMms);
192        tmpFolder.setHasImContent(hasIM);
193        tmpFolder.setHasEmailContent(hasEmail);
194
195        // Add the mandatory folders
196        addBaseFolders(tmpFolder);
197        if(mEnableSmsMms) {
198            addSmsMmsFolders(tmpFolder);
199        }
200        if(hasEmail) {
201            if (D) Log.d(TAG, "buildFolderStructure(): " + mEmailFolderUri.toString());
202            addEmailFolders(tmpFolder);
203        }
204        if (hasIM) {
205            addImFolders(tmpFolder);
206        }
207    }
208
209    /**
210     * Add
211     * @param root
212     */
213    private void addBaseFolders(BluetoothMapFolderElement root) {
214        root.addFolder(BluetoothMapContract.FOLDER_NAME_INBOX);         // root/telecom/msg/inbox
215        root.addFolder(BluetoothMapContract.FOLDER_NAME_OUTBOX);
216        root.addFolder(BluetoothMapContract.FOLDER_NAME_SENT);
217        root.addFolder(BluetoothMapContract.FOLDER_NAME_DELETED);
218    }
219
220    /**
221     * Add
222     * @param root
223     */
224    private void addSmsMmsFolders(BluetoothMapFolderElement root) {
225        root.addSmsMmsFolder(BluetoothMapContract.FOLDER_NAME_INBOX);   // root/telecom/msg/inbox
226        root.addSmsMmsFolder(BluetoothMapContract.FOLDER_NAME_OUTBOX);
227        root.addSmsMmsFolder(BluetoothMapContract.FOLDER_NAME_SENT);
228        root.addSmsMmsFolder(BluetoothMapContract.FOLDER_NAME_DELETED);
229        root.addSmsMmsFolder(BluetoothMapContract.FOLDER_NAME_DRAFT);
230    }
231
232
233    private void addImFolders(BluetoothMapFolderElement root) throws RemoteException {
234        // Select all parent folders
235        root.addImFolder(BluetoothMapContract.FOLDER_NAME_INBOX,
236                BluetoothMapContract.FOLDER_ID_INBOX);       // root/telecom/msg/inbox
237        root.addImFolder(BluetoothMapContract.FOLDER_NAME_OUTBOX,
238                BluetoothMapContract.FOLDER_ID_OUTBOX);
239        root.addImFolder(BluetoothMapContract.FOLDER_NAME_SENT,
240                BluetoothMapContract.FOLDER_ID_SENT);
241        root.addImFolder(BluetoothMapContract.FOLDER_NAME_DELETED,
242                BluetoothMapContract.FOLDER_ID_DELETED);
243        root.addImFolder(BluetoothMapContract.FOLDER_NAME_DRAFT,
244                BluetoothMapContract.FOLDER_ID_DRAFT);
245    }
246
247    /**
248     * Recursively adds folders based on the folders in the email content provider.
249     *       Add a content observer? - to refresh the folder list if any change occurs.
250     *       Consider simply deleting the entire table, and then rebuild using
251     *       buildFolderStructure()
252     *       WARNING: there is no way to notify the client about these changes - hence
253     *       we need to either keep the folder structure constant, disconnect or fail anything
254     *       referring to currentFolder.
255     *       It is unclear what to set as current folder to be able to go one level up...
256     *       The best solution would be to keep the folder structure constant during a connection.
257     * @param folder the parent folder to which subFolders needs to be added. The
258     *        folder.getFolderId() will be used to query sub-folders.
259     *        Use a parentFolder with id -1 to get all folders from root.
260     */
261    private void addEmailFolders(BluetoothMapFolderElement parentFolder) throws RemoteException {
262        // Select all parent folders
263        BluetoothMapFolderElement newFolder;
264
265        String where = BluetoothMapContract.FolderColumns.PARENT_FOLDER_ID +
266                        " = " + parentFolder.getFolderId();
267        Cursor c = mProviderClient.query(mEmailFolderUri,
268                        BluetoothMapContract.BT_FOLDER_PROJECTION, where, null, null);
269        try {
270            if (c != null) {
271                c.moveToPosition(-1);
272                while (c.moveToNext()) {
273                    String name = c.getString(c.getColumnIndex(
274                            BluetoothMapContract.FolderColumns.NAME));
275                    long id = c.getLong(c.getColumnIndex(BluetoothMapContract.FolderColumns._ID));
276                    newFolder = parentFolder.addEmailFolder(name, id);
277                    addEmailFolders(newFolder); // Use recursion to add any sub folders
278                }
279
280            } else {
281                if (D) Log.d(TAG, "addEmailFolders(): no elements found");
282            }
283        } finally {
284            if (c != null) c.close();
285        }
286    }
287
288    @Override
289    public boolean isSrmSupported() {
290        // TODO: Update based on the transport used
291        return true;
292    }
293
294    public int getRemoteFeatureMask() {
295        return mRemoteFeatureMask;
296    }
297
298    public void setRemoteFeatureMask(int mRemoteFeatureMask) {
299        if(D) Log.d(TAG, "setRemoteFeatureMask() " + Integer.toHexString(mRemoteFeatureMask));
300        this.mRemoteFeatureMask = mRemoteFeatureMask;
301        this.mOutContent.setRemoteFeatureMask(mRemoteFeatureMask);
302    }
303
304    @Override
305    public int onConnect(final HeaderSet request, HeaderSet reply) {
306        if (D) Log.d(TAG, "onConnect():");
307        if (V) logHeader(request);
308        mThreadIdSupport = false; // Always assume not supported at new connect.
309        mMessageVersion = BluetoothMapUtils.MAP_V10_STR;//always assume version 1.0 to start with
310        notifyUpdateWakeLock();
311        Long threadedMailKey = null;
312        try {
313            byte[] uuid = (byte[])request.getHeader(HeaderSet.TARGET);
314            threadedMailKey = (Long)request.getHeader(THREADED_MAIL_HEADER_ID);
315            if (uuid == null) {
316                return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE;
317            }
318            if (D) Log.d(TAG, "onConnect(): uuid=" + Arrays.toString(uuid));
319
320            if (uuid.length != UUID_LENGTH) {
321                Log.w(TAG, "Wrong UUID length");
322                return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE;
323            }
324            for (int i = 0; i < UUID_LENGTH; i++) {
325                if (uuid[i] != MAP_TARGET[i]) {
326                    Log.w(TAG, "Wrong UUID");
327                    return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE;
328                }
329            }
330            reply.setHeader(HeaderSet.WHO, uuid);
331        } catch (IOException e) {
332            Log.e(TAG,"Exception during onConnect:", e);
333            return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
334        }
335
336        try {
337            byte[] remote = (byte[])request.getHeader(HeaderSet.WHO);
338            if (remote != null) {
339                if (D) Log.d(TAG, "onConnect(): remote=" + Arrays.toString(remote));
340                reply.setHeader(HeaderSet.TARGET, remote);
341            }
342            if(threadedMailKey != null && threadedMailKey.longValue() == THREAD_MAIL_KEY)
343            {
344                /* If the client provides the correct key we enable threaded e-mail support
345                 * and reply to the client that we support the requested feature.
346                 * This is currently an Android only feature. */
347                mThreadIdSupport = true;
348                reply.setHeader(THREADED_MAIL_HEADER_ID, THREAD_MAIL_KEY);
349            }
350        } catch (IOException e) {
351            Log.e(TAG,"Exception during onConnect:", e);
352            mThreadIdSupport = false;
353            return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
354        }
355
356        if((mRemoteFeatureMask & BluetoothMapUtils.MAP_FEATURE_MESSAGE_LISTING_FORMAT_V11_BIT)
357                == BluetoothMapUtils.MAP_FEATURE_MESSAGE_LISTING_FORMAT_V11_BIT) {
358            mThreadIdSupport = true;
359        }
360
361        if((mRemoteFeatureMask & BluetoothMapUtils.MAP_FEATURE_MESSAGE_FORMAT_V11_BIT)
362                == BluetoothMapUtils.MAP_FEATURE_MESSAGE_FORMAT_V11_BIT){
363            mMessageVersion = BluetoothMapUtils.MAP_V11_STR;
364        }
365
366        if (V) Log.v(TAG, "onConnect(): uuid is ok, will send out " +
367                "MSG_SESSION_ESTABLISHED msg.");
368
369        if(mCallback != null) {
370            Message msg = Message.obtain(mCallback);
371            msg.what = BluetoothMapService.MSG_SESSION_ESTABLISHED;
372            msg.sendToTarget();
373        }
374
375        return ResponseCodes.OBEX_HTTP_OK;
376    }
377
378    @Override
379    public void onDisconnect(final HeaderSet req, final HeaderSet resp) {
380        if (D) Log.d(TAG, "onDisconnect(): enter");
381        if (V) logHeader(req);
382        notifyUpdateWakeLock();
383        resp.responseCode = ResponseCodes.OBEX_HTTP_OK;
384        if (mCallback != null) {
385            Message msg = Message.obtain(mCallback);
386            msg.what = BluetoothMapService.MSG_SESSION_DISCONNECTED;
387            msg.sendToTarget();
388            if (V) Log.v(TAG, "onDisconnect(): msg MSG_SESSION_DISCONNECTED sent out.");
389        }
390    }
391
392    @Override
393    public int onAbort(HeaderSet request, HeaderSet reply) {
394        if (D) Log.d(TAG, "onAbort(): enter.");
395        notifyUpdateWakeLock();
396        mIsAborted = true;
397        return ResponseCodes.OBEX_HTTP_OK;
398    }
399
400    @Override
401    public int onPut(final Operation op) {
402        if (D) Log.d(TAG, "onPut(): enter");
403        mIsAborted = false;
404        notifyUpdateWakeLock();
405        HeaderSet request = null;
406        String type, name;
407        byte[] appParamRaw;
408        BluetoothMapAppParams appParams = null;
409
410        try {
411            request = op.getReceivedHeader();
412            if (V) logHeader(request);
413            type = (String)request.getHeader(HeaderSet.TYPE);
414            name = (String)request.getHeader(HeaderSet.NAME);
415            appParamRaw = (byte[])request.getHeader(HeaderSet.APPLICATION_PARAMETER);
416            if(appParamRaw != null)
417                appParams = new BluetoothMapAppParams(appParamRaw);
418            if(D) Log.d(TAG,"type = " + type + ", name = " + name);
419            if (type.equals(TYPE_MESSAGE_UPDATE)) {
420                if(V) {
421                    Log.d(TAG,"TYPE_MESSAGE_UPDATE:");
422                }
423                return updateInbox();
424            }else if(type.equals(TYPE_SET_NOTIFICATION_REGISTRATION)) {
425                if(V) {
426                    Log.d(TAG,"TYPE_SET_NOTIFICATION_REGISTRATION: NotificationStatus: "
427                            + appParams.getNotificationStatus());
428                }
429                return mObserver.setNotificationRegistration(appParams.getNotificationStatus());
430            }else if(type.equals(TYPE_SET_NOTIFICATION_FILTER)) {
431                if(V) {
432                    Log.d(TAG,"TYPE_SET_NOTIFICATION_FILTER: NotificationFilter: "
433                            + appParams.getNotificationFilter());
434                }
435                mObserver.setNotificationFilter(appParams.getNotificationFilter());
436                return ResponseCodes.OBEX_HTTP_OK;
437            } else if(type.equals(TYPE_SET_MESSAGE_STATUS)) {
438                if(V) {
439                    Log.d(TAG,"TYPE_SET_MESSAGE_STATUS: " +
440                              "StatusIndicator: " + appParams.getStatusIndicator()
441                            + ", StatusValue: " + appParams.getStatusValue()
442                            + ", ExtentedData: " + "" ); // TODO:   appParams.getExtendedImData());
443                }
444                return setMessageStatus(name, appParams);
445            } else if (type.equals(TYPE_MESSAGE)) {
446                if(V) {
447                    Log.d(TAG,"TYPE_MESSAGE: Transparet: " + appParams.getTransparent()
448                            + ", retry: " + appParams.getRetry()
449                            + ", charset: " + appParams.getCharset());
450                }
451                return pushMessage(op, name, appParams, mMessageVersion);
452            } else if (type.equals(TYPE_SET_OWNER_STATUS)) {
453                if(V) {
454                    Log.d(TAG,"TYPE_SET_OWNER_STATUS:" +
455                          " PresenceAvailability " + appParams.getPresenceAvailability() +
456                          ", PresenceStatus: " + appParams.getPresenceStatus() +
457                          ", LastActivity: " + appParams.getLastActivityString() +
458                          ", ChatStatus: " + appParams.getChatState() +
459                          ", ChatStatusConvoId: " + appParams.getChatStateConvoIdString());
460                }
461                return setOwnerStatus(name, appParams);
462            }
463
464        } catch (RemoteException e){
465            //reload the providerClient and return error
466            try {
467                mProviderClient = acquireUnstableContentProviderOrThrow();
468            }catch (RemoteException e2){
469                //should not happen
470            }
471            return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
472        }catch (Exception e) {
473
474            if(D) {
475                Log.e(TAG, "Exception occured while handling request",e);
476            } else {
477                Log.e(TAG, "Exception occured while handling request");
478            }
479            if(mIsAborted) {
480                return ResponseCodes.OBEX_HTTP_OK;
481            } else {
482                return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
483            }
484        }
485        return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
486    }
487
488    private int updateInbox() throws RemoteException{
489        if (mAccount != null) {
490            BluetoothMapFolderElement inboxFolder = mCurrentFolder.getFolderByName(
491                    BluetoothMapContract.FOLDER_NAME_INBOX);
492            if (inboxFolder != null) {
493                long accountId = mAccountId;
494                if (D) Log.d(TAG,"updateInbox inbox=" + inboxFolder.getName() + "id="
495                        + inboxFolder.getFolderId());
496
497                final Bundle extras = new Bundle(2);
498                if (accountId != -1) {
499                    if (D) Log.d(TAG,"updateInbox accountId=" + accountId);
500                    extras.putLong(BluetoothMapContract.EXTRA_UPDATE_FOLDER_ID,
501                            inboxFolder.getFolderId());
502                    extras.putLong(BluetoothMapContract.EXTRA_UPDATE_ACCOUNT_ID, accountId);
503                } else {
504                    // Only error code allowed on an UpdateInbox is OBEX_HTTP_NOT_IMPLEMENTED,
505                    // i.e. if e.g. update not allowed on the mailbox
506                    if (D) Log.d(TAG,"updateInbox accountId=0 -> OBEX_HTTP_NOT_IMPLEMENTED");
507                    return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
508                }
509
510                Uri emailUri = Uri.parse(mBaseUriString);
511                if (D) Log.d(TAG,"updateInbox in: " + emailUri.toString());
512                try {
513                    if (D) Log.d(TAG,"updateInbox call()...");
514                    Bundle myBundle = mProviderClient.call(
515                            BluetoothMapContract.METHOD_UPDATE_FOLDER, null, extras);
516                    if (myBundle != null)
517                        return ResponseCodes.OBEX_HTTP_OK;
518                    else {
519                        if (D) Log.d(TAG,"updateInbox call failed");
520                        return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
521                    }
522                } catch (RemoteException e){
523                    mProviderClient = acquireUnstableContentProviderOrThrow();
524                    return ResponseCodes.OBEX_HTTP_UNAVAILABLE;
525                }catch (NullPointerException e) {
526                    if(D) Log.e(TAG, "UpdateInbox - if uri or method is null", e);
527                    return ResponseCodes.OBEX_HTTP_UNAVAILABLE;
528
529                } catch (IllegalArgumentException e) {
530                    if(D) Log.e(TAG, "UpdateInbox - if uri is not known", e);
531                    return ResponseCodes.OBEX_HTTP_UNAVAILABLE;
532                }
533            }
534        }
535
536        return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
537    }
538
539     private BluetoothMapFolderElement getFolderElementFromName(String folderName) {
540        BluetoothMapFolderElement folderElement = null;
541
542        if(folderName == null || folderName.trim().isEmpty() ) {
543            folderElement = mCurrentFolder;
544            if(D) Log.d(TAG, "no folder name supplied, setting folder to current: "
545                             + folderElement.getName());
546        } else {
547            folderElement = mCurrentFolder.getSubFolder(folderName);
548            if (folderElement != null) {
549                if(D) Log.d(TAG, "Folder name: " + folderName +
550                                 " resulted in this element: " + folderElement.getName());
551            }
552        }
553        return folderElement;
554    }
555
556    private int pushMessage(final Operation op, String folderName,
557            BluetoothMapAppParams appParams, String messageVersion) {
558        if(appParams.getCharset() == BluetoothMapAppParams.INVALID_VALUE_PARAMETER) {
559            if(D) Log.d(TAG, "pushMessage: Missing charset - unable to decode message content. " +
560                    "appParams.getCharset() = " + appParams.getCharset());
561            return ResponseCodes.OBEX_HTTP_PRECON_FAILED;
562        }
563        InputStream bMsgStream = null;
564        try {
565            BluetoothMapFolderElement folderElement = getFolderElementFromName(folderName);
566            if(folderElement == null) {
567                Log.w(TAG,"pushMessage: folderElement == null - sending OBEX_HTTP_PRECON_FAILED");
568                return ResponseCodes.OBEX_HTTP_PRECON_FAILED;
569            } else {
570                folderName = folderElement.getName();
571            }
572            if (!folderName.equalsIgnoreCase(BluetoothMapContract.FOLDER_NAME_OUTBOX) &&
573                    !folderName.equalsIgnoreCase(BluetoothMapContract.FOLDER_NAME_DRAFT)) {
574                if(D) Log.d(TAG, "pushMessage: Is only allowed to outbox and draft. " +
575                        "folderName=" + folderName);
576                return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE;
577            }
578
579            /*  - Read out the message
580             *  - Decode into a bMessage
581             *  - send it.
582             */
583            BluetoothMapbMessage message;
584            bMsgStream = op.openInputStream();
585            // Decode the messageBody
586            message = BluetoothMapbMessage.parse(bMsgStream, appParams.getCharset());
587            message.setVersionString(messageVersion);
588            // Send message
589            if (mObserver == null || message == null) {
590                // Should not happen except at shutdown.
591                if(D) Log.w(TAG, "mObserver or parsed message not available" );
592                return ResponseCodes.OBEX_HTTP_UNAVAILABLE;
593            }
594
595            if ((message.getType().equals(TYPE.EMAIL) && (folderElement.getFolderId() == -1))
596                    || ((message.getType().equals(TYPE.SMS_GSM) ||
597                         message.getType().equals(TYPE.SMS_CDMA) ||
598                         message.getType().equals(TYPE.MMS))
599                         && !folderElement.hasSmsMmsContent()) ) {
600                if(D) Log.w(TAG, "Wrong message type recieved" );
601                return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE;
602            }
603
604            long handle = mObserver.pushMessage(message, folderElement, appParams, mBaseUriString);
605            if (D) Log.d(TAG, "pushMessage handle: " + handle);
606            if (handle < 0) {
607                if(D) Log.w(TAG, "Message  handle not created" );
608                return ResponseCodes.OBEX_HTTP_UNAVAILABLE; // Should not happen.
609            }
610            HeaderSet replyHeaders = new HeaderSet();
611            String handleStr = BluetoothMapUtils.getMapHandle(handle, message.getType());
612            if (D) Log.d(TAG, "handleStr: " + handleStr + " message.getType(): "
613                               + message.getType());
614            replyHeaders.setHeader(HeaderSet.NAME, handleStr);
615            op.sendHeaders(replyHeaders);
616
617        } catch (RemoteException e) {
618            //reload the providerClient and return error
619            try {
620                mProviderClient = acquireUnstableContentProviderOrThrow();
621            }catch (RemoteException e2){
622                //should not happen
623                if(D) Log.w(TAG, "acquireUnstableContentProviderOrThrow FAILED");
624            }
625            return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
626        } catch (IllegalArgumentException e) {
627            if (D) Log.e(TAG, "Wrongly formatted bMessage received", e);
628            return ResponseCodes.OBEX_HTTP_PRECON_FAILED;
629        } catch (IOException e) {
630            if (D) Log.e(TAG, "Exception occured: ", e);
631            if(mIsAborted == true) {
632                if(D) Log.d(TAG, "PushMessage Operation Aborted");
633                return ResponseCodes.OBEX_HTTP_OK;
634            } else {
635                return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
636            }
637        } catch (Exception e) {
638            if (D) Log.e(TAG, "Exception:", e);
639            return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
640        } finally {
641            if(bMsgStream != null) {
642                try {
643                    bMsgStream.close();
644                } catch (IOException e) {}
645            }
646        }
647        return ResponseCodes.OBEX_HTTP_OK;
648    }
649
650    private int setMessageStatus(String msgHandle, BluetoothMapAppParams appParams) {
651        int indicator = appParams.getStatusIndicator();
652        int value = appParams.getStatusValue();
653        String extendedData = ""; // TODO: appParams.getExtendedImData();
654
655        long handle;
656        BluetoothMapUtils.TYPE msgType;
657
658        if (msgHandle == null) {
659            return ResponseCodes.OBEX_HTTP_PRECON_FAILED;
660        } else if ((indicator == BluetoothMapAppParams.INVALID_VALUE_PARAMETER ||
661                   value == BluetoothMapAppParams.INVALID_VALUE_PARAMETER) &&
662                   extendedData == null) {
663            return ResponseCodes.OBEX_HTTP_PRECON_FAILED;
664        }
665        if (mObserver == null) {
666            if(D) Log.e(TAG, "Error: no mObserver!");
667            return ResponseCodes.OBEX_HTTP_UNAVAILABLE; // Should not happen.
668        }
669
670        try {
671            handle = BluetoothMapUtils.getCpHandle(msgHandle);
672            msgType = BluetoothMapUtils.getMsgTypeFromHandle(msgHandle);
673            if(D) Log.d(TAG,"setMessageStatus. Handle:" + handle+", MsgType: "+ msgType);
674        } catch (NumberFormatException e) {
675            Log.w(TAG, "Wrongly formatted message handle: " + msgHandle);
676            return ResponseCodes.OBEX_HTTP_PRECON_FAILED;
677        } catch (IllegalArgumentException e) {
678            Log.w(TAG, "Message type not found in handle string: " + msgHandle);
679            return ResponseCodes.OBEX_HTTP_PRECON_FAILED;
680        }
681
682        if( indicator == BluetoothMapAppParams.STATUS_INDICATOR_DELETED) {
683            if (!mObserver.setMessageStatusDeleted(handle, msgType, mCurrentFolder,
684                    mBaseUriString, value)) {
685                if(D) Log.w(TAG,"setMessageStatusDeleted failed");
686                return ResponseCodes.OBEX_HTTP_UNAVAILABLE;
687            }
688        } else if( indicator == BluetoothMapAppParams.STATUS_INDICATOR_READ) {
689            try {
690                if (!mObserver.setMessageStatusRead(handle, msgType, mBaseUriString, value)) {
691                    if(D) Log.w(TAG,"not able to update the message");
692                    return ResponseCodes.OBEX_HTTP_UNAVAILABLE;
693                }
694            } catch (RemoteException e) {
695                if(D) Log.w(TAG,"Error in setMessageStatusRead()", e);
696                return ResponseCodes.OBEX_HTTP_UNAVAILABLE;
697            }
698        }
699        if (extendedData != null) {
700
701        }
702
703        return ResponseCodes.OBEX_HTTP_OK;
704    }
705
706    private int setOwnerStatus(String msgHandle, BluetoothMapAppParams appParams)
707            throws RemoteException{
708        // This does only work for IM
709        if (mAccount != null && mAccount.getType() == BluetoothMapUtils.TYPE.IM) {
710            final Bundle extras = new Bundle(5);
711
712            int presenceState = appParams.getPresenceAvailability();
713            String presenceStatus = appParams.getPresenceStatus();
714            long lastActivity = appParams.getLastActivity();
715            int chatState = appParams.getChatState();
716            String chatStatusConvoId = appParams.getChatStateConvoIdString();
717
718            if(presenceState == BluetoothMapAppParams.INVALID_VALUE_PARAMETER &&
719               presenceStatus == null &&
720               lastActivity == BluetoothMapAppParams.INVALID_VALUE_PARAMETER &&
721               chatState == BluetoothMapAppParams.INVALID_VALUE_PARAMETER &&
722               chatStatusConvoId == null) {
723                return ResponseCodes.OBEX_HTTP_PRECON_FAILED;
724            }
725
726            if(presenceState != BluetoothMapAppParams.INVALID_VALUE_PARAMETER) {
727                extras.putInt(BluetoothMapContract.EXTRA_PRESENCE_STATE, presenceState);
728            }
729            if (presenceStatus != null){
730                extras.putString(BluetoothMapContract.EXTRA_PRESENCE_STATUS, presenceStatus);
731            }
732            if (lastActivity != BluetoothMapAppParams.INVALID_VALUE_PARAMETER){
733                extras.putLong(BluetoothMapContract.EXTRA_LAST_ACTIVE, lastActivity);
734            }
735            if (chatState != BluetoothMapAppParams.INVALID_VALUE_PARAMETER &&
736                chatStatusConvoId != null){
737                extras.putInt(BluetoothMapContract.EXTRA_CHAT_STATE, chatState);
738                extras.putString(BluetoothMapContract.EXTRA_CONVERSATION_ID, chatStatusConvoId);
739            }
740
741            Uri uri = Uri.parse(mBaseUriString);
742            if (D) Log.d(TAG,"setOwnerStatus in: " + uri.toString());
743            try {
744                if (D) Log.d(TAG,"setOwnerStatus call()...");
745                Bundle myBundle = mProviderClient.call(
746                        BluetoothMapContract.METHOD_SET_OWNER_STATUS, null, extras);
747                if (myBundle != null)
748                    return ResponseCodes.OBEX_HTTP_OK;
749                else {
750                    if (D) Log.d(TAG,"setOwnerStatus call failed");
751                    return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
752                }
753            } catch (RemoteException e){
754                mProviderClient = acquireUnstableContentProviderOrThrow();
755                return ResponseCodes.OBEX_HTTP_UNAVAILABLE;
756            } catch (NullPointerException e) {
757                if(D) Log.e(TAG, "setOwnerStatus - if uri or method is null", e);
758                return ResponseCodes.OBEX_HTTP_UNAVAILABLE;
759            } catch (IllegalArgumentException e) {
760                if(D) Log.e(TAG, "setOwnerStatus - if uri is not known", e);
761                return ResponseCodes.OBEX_HTTP_UNAVAILABLE;
762            }
763        }
764        return ResponseCodes.OBEX_HTTP_UNAVAILABLE;
765    }
766
767    @Override
768    public int onSetPath(final HeaderSet request, final HeaderSet reply, final boolean backup,
769            final boolean create) {
770        String folderName;
771        BluetoothMapFolderElement folder;
772        notifyUpdateWakeLock();
773        try {
774            folderName = (String)request.getHeader(HeaderSet.NAME);
775        } catch (Exception e) {
776            if(D) {
777                Log.e(TAG, "request headers error" , e);
778            } else {
779                Log.e(TAG, "request headers error");
780            }
781            return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
782        }
783
784        if (V) logHeader(request);
785        if (D) Log.d(TAG, "onSetPath name is " + folderName +
786                          " backup: " + backup +
787                          " create: " + create);
788
789        if(backup == true){
790            if(mCurrentFolder.getParent() != null)
791                mCurrentFolder = mCurrentFolder.getParent();
792            else
793                return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
794        }
795
796        if (folderName == null || folderName.trim().isEmpty()) {
797            if(backup == false)
798                mCurrentFolder = mCurrentFolder.getRoot();
799        }
800        else {
801            folder = mCurrentFolder.getSubFolder(folderName);
802            if(folder != null)
803                mCurrentFolder = folder;
804            else
805                return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
806        }
807        if (V) Log.d(TAG, "Current Folder: " + mCurrentFolder.getName());
808        return ResponseCodes.OBEX_HTTP_OK;
809    }
810
811    @Override
812    public void onClose() {
813        if (mCallback != null) {
814            Message msg = Message.obtain(mCallback);
815            msg.what = BluetoothMapService.MSG_SERVERSESSION_CLOSE;
816            msg.arg1 = mMasId;
817            msg.sendToTarget();
818            if (D) Log.d(TAG, "onClose(): msg MSG_SERVERSESSION_CLOSE sent out.");
819
820        }
821        if(mProviderClient != null){
822            mProviderClient.release();
823            mProviderClient = null;
824        }
825
826    }
827
828    @Override
829    public int onGet(Operation op) {
830        notifyUpdateWakeLock();
831        mIsAborted = false;
832        HeaderSet request;
833        String type;
834        String name;
835        byte[] appParamRaw = null;
836        BluetoothMapAppParams appParams = null;
837        try {
838            request = op.getReceivedHeader();
839            type = (String)request.getHeader(HeaderSet.TYPE);
840
841            appParamRaw = (byte[])request.getHeader(HeaderSet.APPLICATION_PARAMETER);
842            if(appParamRaw != null)
843                appParams = new BluetoothMapAppParams(appParamRaw);
844
845            if (V) logHeader(request);
846            if (D) Log.d(TAG, "OnGet type is " + type );
847
848            if (type == null) {
849                if (V) Log.d(TAG, "type is null?" + type);
850                return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
851            }
852
853            if (type.equals(TYPE_GET_FOLDER_LISTING)) {
854                if (V && appParams != null) {
855                    Log.d(TAG,"TYPE_GET_FOLDER_LISTING: MaxListCount = "
856                            + appParams.getMaxListCount()
857                            + ", ListStartOffset = " + appParams.getStartOffset());
858                }
859                // Block until all packets have been send.
860                return sendFolderListingRsp(op, appParams);
861            } else if (type.equals(TYPE_GET_MESSAGE_LISTING)){
862                name = (String)request.getHeader(HeaderSet.NAME);
863                if (V && appParams != null) {
864                    Log.d(TAG,"TYPE_GET_MESSAGE_LISTING: folder name is: " + name +
865                            ", MaxListCount = " + appParams.getMaxListCount() +
866                            ", ListStartOffset = " + appParams.getStartOffset());
867                    Log.d(TAG,"SubjectLength = " + appParams.getSubjectLength() +
868                            ", ParameterMask = " + appParams.getParameterMask());
869                    Log.d(TAG,"FilterMessageType = " + appParams.getFilterMessageType() );
870                    Log.d(TAG,"FilterPeriodBegin = " + appParams.getFilterPeriodBeginString() +
871                            ", FilterPeriodEnd = " + appParams.getFilterPeriodEndString() +
872                            ", FilterReadStatus = " + appParams.getFilterReadStatus());
873                    Log.d(TAG,"FilterRecipient = " + appParams.getFilterRecipient() +
874                            ", FilterOriginator = " + appParams.getFilterOriginator());
875                    Log.d(TAG,"FilterPriority = " + appParams.getFilterPriority());
876                    long tmpLong = appParams.getFilterMsgHandle();
877                    Log.d(TAG,"FilterMsgHandle = " + (
878                            (tmpLong == BluetoothMapAppParams.INVALID_VALUE_PARAMETER) ? "" :
879                                Long.toHexString(tmpLong)));
880                    SignedLongLong tmpLongLong = appParams.getFilterConvoId();
881                    Log.d(TAG,"FilterConvoId = " + ((tmpLongLong == null) ? "" :
882                        Long.toHexString(tmpLongLong.getLeastSignificantBits()) ) );
883                }
884                // Block until all packets have been send.
885                return sendMessageListingRsp(op, appParams, name);
886
887            } else if (type.equals(TYPE_GET_CONVO_LISTING)){
888                name = (String)request.getHeader(HeaderSet.NAME);
889                if (V && appParams != null) {
890                    Log.d(TAG,"TYPE_GET_CONVO_LISTING: name is" + name +
891                            ", MaxListCount = " + appParams.getMaxListCount() +
892                            ", ListStartOffset = " + appParams.getStartOffset());
893                    Log.d(TAG,"FilterLastActivityBegin = "+appParams.getFilterLastActivityBegin());
894                    Log.d(TAG,"FilterLastActivityEnd = " + appParams.getFilterLastActivityEnd());
895                    Log.d(TAG,"FilterReadStatus = " + appParams.getFilterReadStatus());
896                    Log.d(TAG,"FilterRecipient = " + appParams.getFilterRecipient());
897                }
898                // Block until all packets have been send.
899                return sendConvoListingRsp(op, appParams,name);
900            } else if (type.equals(TYPE_GET_MAS_INSTANCE_INFORMATION)) {
901                if(V && appParams != null) {
902                    Log.d(TAG,"TYPE_MESSAGE (GET): MASInstandeId = "
903                            + appParams.getMasInstanceId());
904                }
905                // Block until all packets have been send.
906                return sendMASInstanceInformationRsp(op, appParams);
907            } else if (type.equals(TYPE_MESSAGE)){
908                name = (String)request.getHeader(HeaderSet.NAME);
909                if(V && appParams != null) {
910                    Log.d(TAG,"TYPE_MESSAGE (GET): name is" + name +
911                            ", Attachment = " + appParams.getAttachment() +
912                            ", Charset = " + appParams.getCharset() +
913                            ", FractionRequest = " + appParams.getFractionRequest());
914                }
915                // Block until all packets have been send.
916                return sendGetMessageRsp(op, name, appParams, mMessageVersion);
917            } else {
918                Log.w(TAG, "unknown type request: " + type);
919                return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE;
920            }
921
922        } catch (IllegalArgumentException e) {
923            Log.e(TAG, "Exception:", e);
924            return ResponseCodes.OBEX_HTTP_PRECON_FAILED;
925        } catch (ParseException e) {
926            Log.e(TAG, "Exception:", e);
927            return ResponseCodes.OBEX_HTTP_PRECON_FAILED;
928        } catch (Exception e) {
929            if(D) {
930                Log.e(TAG, "Exception occured while handling request",e);
931            } else {
932                Log.e(TAG, "Exception occured while handling request");
933            }
934            if(mIsAborted == true) {
935                if(D) Log.d(TAG, "onGet Operation Aborted");
936                return ResponseCodes.OBEX_HTTP_OK;
937            } else {
938                return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
939            }
940        }
941    }
942
943    /**
944     * Generate and send the message listing response based on an application
945     * parameter header. This function call will block until complete or aborted
946     * by the peer. Fragmentation of packets larger than the obex packet size
947     * will be handled by this function.
948     *
949     * @param op
950     *            The OBEX operation.
951     * @param appParams
952     *            The application parameter header
953     * @return {@link ResponseCodes.OBEX_HTTP_OK} on success or
954     *         {@link ResponseCodes.OBEX_HTTP_BAD_REQUEST} on error.
955     */
956    private int sendMessageListingRsp(Operation op,
957                                      BluetoothMapAppParams appParams,
958                                      String folderName){
959        OutputStream outStream = null;
960        byte[] outBytes = null;
961        int maxChunkSize, bytesToWrite, bytesWritten = 0, listSize;
962        boolean hasUnread = false;
963        HeaderSet replyHeaders = new HeaderSet();
964        BluetoothMapAppParams outAppParams = new BluetoothMapAppParams();
965        BluetoothMapMessageListing outList;
966        if(appParams == null){
967            appParams = new BluetoothMapAppParams();
968            appParams.setMaxListCount(1024);
969            appParams.setStartOffset(0);
970        }
971
972        /* MAP Spec 1.3 introduces the following
973         * Messagehandle filtering:
974         * msgListing (messageHandle=X) -> other allowed filters: parametereMask, subjectMaxLength
975         * ConversationID filtering:
976         * msgListing (convoId empty) -> should work as normal msgListing in valid folders
977         * msgListing (convoId=0, no other filters) -> should return all messages in all folders
978         * msgListing (convoId=N, other filters) -> should return all messages in conversationID=N
979         *                                          according to filters requested
980         */
981        BluetoothMapFolderElement folderToList = null;
982        if (appParams.getFilterMsgHandle() != BluetoothMapAppParams.INVALID_VALUE_PARAMETER
983            || appParams.getFilterConvoId() != null) {
984            // If messageHandle or convoId filtering ignore folder
985            Log.v(TAG,"sendMessageListingRsp: ignore folder ");
986            folderToList = mCurrentFolder.getRoot();
987            folderToList.setIngore(true);
988        } else {
989            folderToList = getFolderElementFromName(folderName);
990            if(folderToList == null) {
991                Log.w(TAG,"sendMessageListingRsp: folderToList == "+
992                        "null-sending OBEX_HTTP_BAD_REQUEST");
993                return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
994            }
995            Log.v(TAG,"sendMessageListingRsp: has sms " + folderToList.hasSmsMmsContent() +
996                    "has email " + folderToList.hasEmailContent() +
997                    "has IM " + folderToList.hasImContent() );
998        }
999
1000        try {
1001            // Open the OBEX body stream
1002            outStream = op.openOutputStream();
1003
1004            if(appParams.getMaxListCount() == BluetoothMapAppParams.INVALID_VALUE_PARAMETER)
1005                appParams.setMaxListCount(1024);
1006
1007            if(appParams.getStartOffset() == BluetoothMapAppParams.INVALID_VALUE_PARAMETER)
1008                appParams.setStartOffset(0);
1009
1010            // Check to see if we only need to send the size - hence no need to encode.
1011            if(appParams.getMaxListCount() != 0) {
1012                outList = mOutContent.msgListing(folderToList, appParams);
1013                // Generate the byte stream
1014                outAppParams.setMessageListingSize(outList.getCount());
1015                String version;
1016                if(0 < (mRemoteFeatureMask &
1017                        BluetoothMapUtils.MAP_FEATURE_MESSAGE_LISTING_FORMAT_V11_BIT)) {
1018                    version = BluetoothMapUtils.MAP_V11_STR;
1019                } else {
1020                    version = BluetoothMapUtils.MAP_V10_STR;
1021                }
1022                /* This will only set the version, the bit must also be checked before adding any
1023                 * 1.1 bits to the listing. */
1024                outBytes = outList.encode(mThreadIdSupport, version);
1025                hasUnread = outList.hasUnread();
1026            } else {
1027                listSize = mOutContent.msgListingSize(folderToList, appParams);
1028                hasUnread = mOutContent.msgListingHasUnread(folderToList, appParams);
1029                outAppParams.setMessageListingSize(listSize);
1030                op.noBodyHeader();
1031            }
1032            folderToList.setIngore(false);
1033            // Build the application parameter header
1034            // let the peer know if there are unread messages in the list
1035            if(hasUnread) {
1036                outAppParams.setNewMessage(1);
1037            }else{
1038                outAppParams.setNewMessage(0);
1039            }
1040            if ((mRemoteFeatureMask & BluetoothMapUtils.MAP_FEATURE_DATABASE_INDENTIFIER_BIT)
1041                    == BluetoothMapUtils.MAP_FEATURE_DATABASE_INDENTIFIER_BIT ) {
1042                outAppParams.setDatabaseIdentifier(0, mMasInstance.getDbIdentifier());
1043            }
1044            if((mRemoteFeatureMask & BluetoothMapUtils.MAP_FEATURE_FOLDER_VERSION_COUNTER_BIT)
1045                    == BluetoothMapUtils.MAP_FEATURE_FOLDER_VERSION_COUNTER_BIT) {
1046                // Force update of version counter if needed
1047                mObserver.refreshFolderVersionCounter();
1048                outAppParams.setFolderVerCounter(mMasInstance.getFolderVersionCounter(), 0);
1049            }
1050            outAppParams.setMseTime(Calendar.getInstance().getTime().getTime());
1051            replyHeaders.setHeader(HeaderSet.APPLICATION_PARAMETER, outAppParams.EncodeParams());
1052            op.sendHeaders(replyHeaders);
1053
1054        } catch (IOException e) {
1055            Log.w(TAG,"sendMessageListingRsp: IOException - sending OBEX_HTTP_BAD_REQUEST", e);
1056            if(outStream != null) { try { outStream.close(); } catch (IOException ex) {} }
1057            if(mIsAborted == true) {
1058                if(D) Log.d(TAG, "sendMessageListingRsp Operation Aborted");
1059                return ResponseCodes.OBEX_HTTP_OK;
1060            } else {
1061                return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
1062            }
1063        } catch (IllegalArgumentException e) {
1064            Log.w(TAG,"sendMessageListingRsp: IllegalArgumentException"+
1065                                            " - sending OBEX_HTTP_BAD_REQUEST", e);
1066            if(outStream != null) { try { outStream.close(); } catch (IOException ex) {} }
1067            return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
1068        }
1069
1070        maxChunkSize = op.getMaxPacketSize(); // This must be called after setting the headers.
1071        if(outBytes != null) {
1072            try {
1073                while (bytesWritten < outBytes.length && mIsAborted == false) {
1074                    bytesToWrite = Math.min(maxChunkSize, outBytes.length - bytesWritten);
1075                    outStream.write(outBytes, bytesWritten, bytesToWrite);
1076                    bytesWritten += bytesToWrite;
1077                }
1078            } catch (IOException e) {
1079                if(D) Log.w(TAG,e);
1080                // We were probably aborted or disconnected
1081            } finally {
1082                if(outStream != null) { try { outStream.close(); } catch (IOException e) {} }
1083            }
1084            if(bytesWritten != outBytes.length && !mIsAborted) {
1085                Log.w(TAG,"sendMessageListingRsp: bytesWritten != outBytes.length" +
1086                        " - sending OBEX_HTTP_BAD_REQUEST");
1087                return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
1088            }
1089        } else {
1090            if(outStream != null) { try { outStream.close(); } catch (IOException e) {} }
1091        }
1092        return ResponseCodes.OBEX_HTTP_OK;
1093    }
1094
1095    /**
1096     * Update the {@link BluetoothMapAppParams} object message type filter mask to only contain
1097     * message types supported by this mas instance.
1098     * Could the folder be used in stead?
1099     * @param appParams Reference to the object to update
1100     * @param overwrite True: The msgType will be overwritten to match the message types supported
1101     * by this MAS instance. False: any unsupported message types will be masked out.
1102     */
1103    private void setMsgTypeFilterParams(BluetoothMapAppParams appParams, boolean overwrite) {
1104        int masFilterMask = 0;
1105        if(!mEnableSmsMms) {
1106            masFilterMask |= BluetoothMapAppParams.FILTER_NO_SMS_CDMA;
1107            masFilterMask |= BluetoothMapAppParams.FILTER_NO_SMS_GSM;
1108            masFilterMask |= BluetoothMapAppParams.FILTER_NO_MMS;
1109        }
1110        if(mAccount==null){
1111            masFilterMask |= BluetoothMapAppParams.FILTER_NO_EMAIL;
1112            masFilterMask |= BluetoothMapAppParams.FILTER_NO_IM;
1113        } else {
1114            if(!(mAccount.getType() == BluetoothMapUtils.TYPE.EMAIL)) {
1115                masFilterMask |= BluetoothMapAppParams.FILTER_NO_EMAIL;
1116            }
1117            if(!(mAccount.getType() == BluetoothMapUtils.TYPE.IM)) {
1118                masFilterMask |= BluetoothMapAppParams.FILTER_NO_IM;
1119            }
1120        }
1121        if(overwrite) {
1122            appParams.setFilterMessageType(masFilterMask);
1123        } else {
1124            int newMask = appParams.getFilterMessageType();
1125            if(newMask == BluetoothMapAppParams.INVALID_VALUE_PARAMETER) {
1126                appParams.setFilterMessageType(newMask);
1127            } else {
1128                newMask |= masFilterMask;
1129                appParams.setFilterMessageType(newMask);
1130            }
1131        }
1132    }
1133
1134    /**
1135     * Generate and send the Conversation listing response based on an application
1136     * parameter header. This function call will block until complete or aborted
1137     * by the peer. Fragmentation of packets larger than the obex packet size
1138     * will be handled by this function.
1139     *
1140     * @param op
1141     *            The OBEX operation.
1142     * @param appParams
1143     *            The application parameter header
1144     * @return {@link ResponseCodes.OBEX_HTTP_OK} on success or
1145     *         {@link ResponseCodes.OBEX_HTTP_BAD_REQUEST} on error.
1146     */
1147    private int sendConvoListingRsp(Operation op,
1148                                    BluetoothMapAppParams appParams,
1149                                    String folderName){
1150        OutputStream outStream = null;
1151        byte[] outBytes = null;
1152        int maxChunkSize, bytesToWrite, bytesWritten = 0;
1153        //boolean hasUnread = false;
1154        HeaderSet replyHeaders = new HeaderSet();
1155        BluetoothMapAppParams outAppParams = new BluetoothMapAppParams();
1156        BluetoothMapConvoListing outList;
1157        if(appParams == null){
1158            appParams = new BluetoothMapAppParams();
1159            appParams.setMaxListCount(1024);
1160            appParams.setStartOffset(0);
1161        }
1162        // As the app parameters do not carry which message types to list, we set the filter here
1163        // to all message types supported by this instance.
1164        setMsgTypeFilterParams(appParams, true);
1165
1166        // Check to see if we only need to send the size - hence no need to encode.
1167        try {
1168            // Open the OBEX body stream
1169            outStream = op.openOutputStream();
1170
1171            if(appParams.getMaxListCount() == BluetoothMapAppParams.INVALID_VALUE_PARAMETER)
1172                appParams.setMaxListCount(1024);
1173
1174            if(appParams.getStartOffset() == BluetoothMapAppParams.INVALID_VALUE_PARAMETER)
1175                appParams.setStartOffset(0);
1176
1177            if(appParams.getMaxListCount() != 0) {
1178                outList = mOutContent.convoListing(appParams, false);
1179                outAppParams.setConvoListingSize(outList.getCount());
1180                // Generate the byte stream
1181                outBytes = outList.encode(); // Include thread ID for clients that supports it.
1182      //          hasUnread = outList.hasUnread();
1183                if(D) Log.d(TAG, "outBytes size:"+ outBytes.length);
1184            } else {
1185                outList = mOutContent.convoListing(appParams, true);
1186                outAppParams.setConvoListingSize(outList.getCount());
1187                if(mEnableSmsMms) {
1188                    mOutContent.refreshSmsMmsConvoVersions();
1189                }
1190                if(mAccount != null) {
1191                    mOutContent.refreshImEmailConvoVersions();
1192                }
1193                // Force update of version counter if needed
1194                mObserver.refreshConvoListVersionCounter();
1195                if(0 < (mRemoteFeatureMask &
1196                        BluetoothMapUtils.MAP_FEATURE_CONVERSATION_VERSION_COUNTER_BIT)) {
1197                    outAppParams.setConvoListingVerCounter(
1198                            mMasInstance.getCombinedConvoListVersionCounter(), 0);
1199                }
1200                op.noBodyHeader();
1201            }
1202            if(D) Log.d(TAG, "outList size:"+ outList.getCount()
1203                    + " MaxListCount: "+appParams.getMaxListCount());
1204            outList = null; // We don't need it anymore - we might as well give it up for GC
1205            outAppParams.setDatabaseIdentifier(0, mMasInstance.getDbIdentifier());
1206
1207            // Build the application parameter header
1208            // The MseTime is not in the CR - but I think it is missing.
1209            outAppParams.setMseTime(Calendar.getInstance().getTime().getTime());
1210            replyHeaders.setHeader(HeaderSet.APPLICATION_PARAMETER, outAppParams.EncodeParams());
1211            op.sendHeaders(replyHeaders);
1212
1213        } catch (IOException e) {
1214            Log.w(TAG,"sendConvoListingRsp: IOException - sending OBEX_HTTP_BAD_REQUEST", e);
1215            if(outStream != null) { try { outStream.close(); } catch (IOException ex) {} }
1216            if(mIsAborted == true) {
1217                if(D) Log.d(TAG, "sendConvoListingRsp Operation Aborted");
1218                return ResponseCodes.OBEX_HTTP_OK;
1219            } else {
1220                return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
1221            }
1222        } catch (IllegalArgumentException e) {
1223            Log.w(TAG,"sendConvoListingRsp: IllegalArgumentException" +
1224                    " - sending OBEX_HTTP_BAD_REQUEST", e);
1225            if(outStream != null) { try { outStream.close(); } catch (IOException ex) {} }
1226            return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
1227        }
1228
1229        maxChunkSize = op.getMaxPacketSize(); // This must be called after setting the headers.
1230        if(outBytes != null) {
1231            try {
1232                while (bytesWritten < outBytes.length && mIsAborted == false) {
1233                    bytesToWrite = Math.min(maxChunkSize, outBytes.length - bytesWritten);
1234                    outStream.write(outBytes, bytesWritten, bytesToWrite);
1235                    bytesWritten += bytesToWrite;
1236                }
1237            } catch (IOException e) {
1238                if(D) Log.w(TAG,e);
1239                // We were probably aborted or disconnected
1240            } finally {
1241                if(outStream != null) { try { outStream.close(); } catch (IOException e) {} }
1242            }
1243            if(bytesWritten != outBytes.length && !mIsAborted) {
1244                Log.w(TAG,"sendConvoListingRsp: bytesWritten != outBytes.length" +
1245                        " - sending OBEX_HTTP_BAD_REQUEST");
1246                return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
1247            }
1248        } else {
1249            if(outStream != null) { try { outStream.close(); } catch (IOException e) {} }
1250        }
1251        return ResponseCodes.OBEX_HTTP_OK;
1252    }
1253
1254    /**
1255     * Generate and send the Folder listing response based on an application
1256     * parameter header. This function call will block until complete or aborted
1257     * by the peer. Fragmentation of packets larger than the obex packet size
1258     * will be handled by this function.
1259     *
1260     * @param op
1261     *            The OBEX operation.
1262     * @param appParams
1263     *            The application parameter header
1264     * @return {@link ResponseCodes.OBEX_HTTP_OK} on success or
1265     *         {@link ResponseCodes.OBEX_HTTP_BAD_REQUEST} on error.
1266     */
1267    private int sendFolderListingRsp(Operation op, BluetoothMapAppParams appParams){
1268        OutputStream outStream = null;
1269        byte[] outBytes = null;
1270        BluetoothMapAppParams outAppParams = new BluetoothMapAppParams();
1271        int maxChunkSize, bytesWritten = 0;
1272        HeaderSet replyHeaders = new HeaderSet();
1273        int bytesToWrite, maxListCount, listStartOffset;
1274        if(appParams == null){
1275            appParams = new BluetoothMapAppParams();
1276            appParams.setMaxListCount(1024);
1277        }
1278
1279        if(V)
1280            Log.v(TAG,"sendFolderList for " + mCurrentFolder.getName());
1281
1282        try {
1283            maxListCount = appParams.getMaxListCount();
1284            listStartOffset = appParams.getStartOffset();
1285
1286            if(listStartOffset == BluetoothMapAppParams.INVALID_VALUE_PARAMETER)
1287                listStartOffset = 0;
1288
1289            if(maxListCount == BluetoothMapAppParams.INVALID_VALUE_PARAMETER)
1290                maxListCount = 1024;
1291
1292            if(maxListCount != 0)
1293            {
1294                outBytes = mCurrentFolder.encode(listStartOffset, maxListCount);
1295                outStream = op.openOutputStream();
1296            } else {
1297                // ESR08 specified that this shall only be included for MaxListCount=0
1298                outAppParams.setFolderListingSize(mCurrentFolder.getSubFolderCount());
1299                op.noBodyHeader();
1300            }
1301
1302            // Build and set the application parameter header
1303            replyHeaders.setHeader(HeaderSet.APPLICATION_PARAMETER, outAppParams.EncodeParams());
1304            op.sendHeaders(replyHeaders);
1305
1306        } catch (IOException e1) {
1307            Log.w(TAG,"sendFolderListingRsp: IOException" +
1308                    " - sending OBEX_HTTP_BAD_REQUEST Exception:", e1);
1309            if(outStream != null) { try { outStream.close(); } catch (IOException e) {} }
1310            if(mIsAborted == true) {
1311                if(D) Log.d(TAG, "sendFolderListingRsp Operation Aborted");
1312                return ResponseCodes.OBEX_HTTP_OK;
1313            } else {
1314                return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
1315            }
1316        } catch (IllegalArgumentException e1) {
1317            Log.w(TAG,"sendFolderListingRsp: IllegalArgumentException" +
1318                    " - sending OBEX_HTTP_BAD_REQUEST Exception:", e1);
1319            if(outStream != null) { try { outStream.close(); } catch (IOException e) {} }
1320            return ResponseCodes.OBEX_HTTP_PRECON_FAILED;
1321        }
1322
1323        maxChunkSize = op.getMaxPacketSize(); // This must be called after setting the headers.
1324
1325        if(outBytes != null) {
1326            try {
1327                while (bytesWritten < outBytes.length && mIsAborted == false) {
1328                    bytesToWrite = Math.min(maxChunkSize, outBytes.length - bytesWritten);
1329                    outStream.write(outBytes, bytesWritten, bytesToWrite);
1330                    bytesWritten += bytesToWrite;
1331                }
1332            } catch (IOException e) {
1333                // We were probably aborted or disconnected
1334            } finally {
1335                if(outStream != null) { try { outStream.close(); } catch (IOException e) {} }
1336            }
1337            if(V)
1338                Log.v(TAG,"sendFolderList sent " + bytesWritten+" bytes out of "+ outBytes.length);
1339            if(bytesWritten == outBytes.length || mIsAborted)
1340                return ResponseCodes.OBEX_HTTP_OK;
1341            else
1342                return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
1343        }
1344
1345        return ResponseCodes.OBEX_HTTP_OK;
1346    }
1347
1348    /**
1349     * Generate and send the get MAS Instance Information response based on an MAS Instance
1350     *
1351     * @param op
1352     *            The OBEX operation.
1353     * @param appParams
1354     *            The application parameter header
1355     * @return {@link ResponseCodes.OBEX_HTTP_OK} on success or
1356     *         {@link ResponseCodes.OBEX_HTTP_BAD_REQUEST} on error.
1357     */
1358    private int sendMASInstanceInformationRsp(Operation op, BluetoothMapAppParams appParams){
1359
1360        OutputStream outStream = null;
1361        byte[] outBytes = null;
1362        String outString = null;
1363        int maxChunkSize, bytesToWrite, bytesWritten = 0;
1364
1365        try {
1366            if(mMasId == appParams.getMasInstanceId()) {
1367                if(mAccount != null) {
1368                    if(mAccount.getType() == TYPE.EMAIL) {
1369                        outString = (mAccount.getName() != null) ? mAccount.getName() :
1370                            BluetoothMapMasInstance.TYPE_EMAIL_STR;
1371                    } else if(mAccount.getType() == TYPE.IM){
1372                        outString = mAccount.getUciFull();
1373                        if(outString == null) {
1374                            String uci = mAccount.getUci();
1375                            // TODO: Do we need to align this with HF/PBAP
1376                            StringBuilder sb =
1377                                    new StringBuilder(uci == null ? 5 : 5 + uci.length());
1378                            sb.append("un");
1379                            if(mMasId < 10) {
1380                                sb.append("00");
1381                            } else if(mMasId < 100) {
1382                                sb.append("0");
1383                            }
1384                            sb.append(mMasId);
1385                            if(uci != null) {
1386                                sb.append(":").append(uci);
1387                            }
1388                            outString = sb.toString();
1389                        }
1390                    }
1391                } else {
1392                    outString = BluetoothMapMasInstance.TYPE_SMS_MMS_STR;
1393                    // TODO: Add phone number if possible
1394                }
1395            } else {
1396                return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
1397            }
1398
1399            /* Ensure byte array max length is 200 containing valid UTF-8 characters */
1400            outBytes = BluetoothMapUtils.truncateUtf8StringToBytearray(outString,
1401                    MAS_INSTANCE_INFORMATION_LENGTH);
1402
1403            // Open the OBEX body stream
1404            outStream = op.openOutputStream();
1405
1406        } catch (IOException e) {
1407            Log.w(TAG,"sendMASInstanceInformationRsp: IOException" +
1408                    " - sending OBEX_HTTP_BAD_REQUEST", e);
1409            if(mIsAborted == true) {
1410                if(D) Log.d(TAG, "sendMASInstanceInformationRsp Operation Aborted");
1411                return ResponseCodes.OBEX_HTTP_OK;
1412            } else {
1413                return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
1414            }
1415        }
1416
1417        maxChunkSize = op.getMaxPacketSize(); // This must be called after setting the headers.
1418
1419        if(outBytes != null) {
1420            try {
1421                while (bytesWritten < outBytes.length && mIsAborted == false) {
1422                    bytesToWrite = Math.min(maxChunkSize, outBytes.length - bytesWritten);
1423                    outStream.write(outBytes, bytesWritten, bytesToWrite);
1424                    bytesWritten += bytesToWrite;
1425                }
1426            } catch (IOException e) {
1427                // We were probably aborted or disconnected
1428            } finally {
1429                if(outStream != null) { try { outStream.close(); } catch (IOException e) {} }
1430            }
1431            if(V)
1432                Log.v(TAG,"sendMASInstanceInformationRsp sent " + bytesWritten +
1433                        " bytes out of "+ outBytes.length);
1434            if(bytesWritten == outBytes.length || mIsAborted)
1435                return ResponseCodes.OBEX_HTTP_OK;
1436            else
1437                return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
1438        }
1439        return ResponseCodes.OBEX_HTTP_OK;
1440    }
1441
1442    /**
1443     * Generate and send the get message response based on an application
1444     * parameter header and a handle.
1445     *
1446     * @param op
1447     *            The OBEX operation.
1448     * @param handle
1449     *            The handle of the requested message
1450     * @param appParams
1451     *            The application parameter header
1452     * @param version
1453     *              The string representation of the version number(i.e. "1.0")
1454     * @return {@link ResponseCodes.OBEX_HTTP_OK} on success or
1455     *         {@link ResponseCodes.OBEX_HTTP_BAD_REQUEST} on error.
1456     */
1457    private int sendGetMessageRsp(Operation op, String handle,
1458            BluetoothMapAppParams appParams, String version){
1459        OutputStream outStream = null;
1460        byte[] outBytes = null;
1461        int maxChunkSize, bytesToWrite, bytesWritten = 0;
1462
1463        try {
1464            outBytes = mOutContent.getMessage(handle, appParams, mCurrentFolder, version);
1465            outStream = op.openOutputStream();
1466
1467            // If it is a fraction request of Email message, set header before responding
1468            if ((BluetoothMapUtils.getMsgTypeFromHandle(handle).equals(TYPE.EMAIL)||
1469                    (BluetoothMapUtils.getMsgTypeFromHandle(handle).equals(TYPE.IM))) &&
1470                    (appParams.getFractionRequest() ==
1471                    BluetoothMapAppParams.FRACTION_REQUEST_FIRST)) {
1472                BluetoothMapAppParams outAppParams  = new BluetoothMapAppParams();
1473                HeaderSet replyHeaders = new HeaderSet();
1474                outAppParams.setFractionDeliver(BluetoothMapAppParams.FRACTION_DELIVER_LAST);
1475                // Build and set the application parameter header
1476                replyHeaders.setHeader(HeaderSet.APPLICATION_PARAMETER,
1477                        outAppParams.EncodeParams());
1478                op.sendHeaders(replyHeaders);
1479                if(V) Log.v(TAG,"sendGetMessageRsp fractionRequest - " +
1480                        "set FRACTION_DELIVER_LAST header");
1481            }
1482
1483        } catch (IOException e) {
1484            Log.w(TAG,"sendGetMessageRsp: IOException - sending OBEX_HTTP_BAD_REQUEST", e);
1485            if(outStream != null) { try { outStream.close(); } catch (IOException ex) {} }
1486            if(mIsAborted == true) {
1487                if(D) Log.d(TAG, "sendGetMessageRsp Operation Aborted");
1488                return ResponseCodes.OBEX_HTTP_OK;
1489            } else {
1490                return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
1491            }
1492        } catch (IllegalArgumentException e) {
1493            Log.w(TAG,"sendGetMessageRsp: IllegalArgumentException (e.g. invalid handle) - " +
1494                    "sending OBEX_HTTP_BAD_REQUEST", e);
1495            if(outStream != null) { try { outStream.close(); } catch (IOException ex) {} }
1496            return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
1497        }
1498
1499        maxChunkSize = op.getMaxPacketSize(); // This must be called after setting the headers.
1500
1501        if(outBytes != null) {
1502            try {
1503                while (bytesWritten < outBytes.length && mIsAborted == false) {
1504                    bytesToWrite = Math.min(maxChunkSize, outBytes.length - bytesWritten);
1505                    outStream.write(outBytes, bytesWritten, bytesToWrite);
1506                    bytesWritten += bytesToWrite;
1507                }
1508            } catch (IOException e) {
1509                // We were probably aborted or disconnected
1510                if(D && e.getMessage().equals("Abort Received")) {
1511                    Log.w(TAG, "getMessage() Aborted...", e);
1512                }
1513            } finally {
1514                if(outStream != null) { try { outStream.close(); } catch (IOException e) {} }
1515            }
1516            if(bytesWritten == outBytes.length || mIsAborted)
1517                return ResponseCodes.OBEX_HTTP_OK;
1518            else
1519                return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
1520        }
1521
1522        return ResponseCodes.OBEX_HTTP_OK;
1523    }
1524
1525    @Override
1526    public int onDelete(HeaderSet request, HeaderSet reply) {
1527        if(D) Log.v(TAG, "onDelete() " + request.toString());
1528        mIsAborted = false;
1529        notifyUpdateWakeLock();
1530        String type, name;
1531        byte[] appParamRaw;
1532        BluetoothMapAppParams appParams = null;
1533
1534        /* TODO: If this is to be placed here, we need to cleanup - e.g. the exception handling */
1535        try {
1536            type = (String)request.getHeader(HeaderSet.TYPE);
1537
1538            name = (String)request.getHeader(HeaderSet.NAME);
1539            appParamRaw = (byte[])request.getHeader(HeaderSet.APPLICATION_PARAMETER);
1540            if(appParamRaw != null)
1541                appParams = new BluetoothMapAppParams(appParamRaw);
1542            if(D) Log.d(TAG,"type = " + type + ", name = " + name);
1543            if(type.equals(TYPE_SET_NOTIFICATION_FILTER)) {
1544                if(V) {
1545                    Log.d(TAG,"TYPE_SET_NOTIFICATION_FILTER: NotificationFilter: "
1546                            + appParams.getNotificationFilter());
1547                }
1548                mObserver.setNotificationFilter(appParams.getNotificationFilter());
1549                return ResponseCodes.OBEX_HTTP_OK;
1550            }  else if (type.equals(TYPE_SET_OWNER_STATUS)) {
1551                if(V) {
1552                    Log.d(TAG,"TYPE_SET_OWNER_STATUS:" +
1553                          " PresenceAvailability " + appParams.getPresenceAvailability() +
1554                          ", PresenceStatus: " + appParams.getPresenceStatus() +
1555                          ", LastActivity: " + appParams.getLastActivityString() +
1556                          ", ChatStatus: " + appParams.getChatState() +
1557                          ", ChatStatusConvoId: " + appParams.getChatStateConvoIdString());
1558                }
1559                return setOwnerStatus(name, appParams);
1560            }
1561
1562        } catch (RemoteException e){
1563            //reload the providerClient and return error
1564            try {
1565                mProviderClient = acquireUnstableContentProviderOrThrow();
1566            }catch (RemoteException e2){
1567                //should not happen
1568            }
1569            return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
1570        }catch (Exception e) {
1571
1572            if(D) {
1573                Log.e(TAG, "Exception occured while handling request",e);
1574            } else {
1575                Log.e(TAG, "Exception occured while handling request");
1576            }
1577            if(mIsAborted) {
1578                return ResponseCodes.OBEX_HTTP_OK;
1579            } else {
1580                return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
1581            }
1582        }
1583        return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
1584    }
1585
1586    private void notifyUpdateWakeLock() {
1587        if(mCallback != null) {
1588            Message msg = Message.obtain(mCallback);
1589            msg.what = BluetoothMapService.MSG_ACQUIRE_WAKE_LOCK;
1590            msg.sendToTarget();
1591        }
1592    }
1593
1594    private static final void logHeader(HeaderSet hs) {
1595        Log.v(TAG, "Dumping HeaderSet " + hs.toString());
1596        try {
1597            Log.v(TAG, "CONNECTION_ID : " + hs.getHeader(HeaderSet.CONNECTION_ID));
1598            Log.v(TAG, "NAME : " + hs.getHeader(HeaderSet.NAME));
1599            Log.v(TAG, "TYPE : " + hs.getHeader(HeaderSet.TYPE));
1600            Log.v(TAG, "TARGET : " + hs.getHeader(HeaderSet.TARGET));
1601            Log.v(TAG, "WHO : " + hs.getHeader(HeaderSet.WHO));
1602            Log.v(TAG, "APPLICATION_PARAMETER : " + hs.getHeader(HeaderSet.APPLICATION_PARAMETER));
1603        } catch (IOException e) {
1604            Log.e(TAG, "dump HeaderSet error " + e);
1605        }
1606        Log.v(TAG, "NEW!!! Dumping HeaderSet END");
1607    }
1608}
1609