BluetoothMapObexServer.java revision 165d7facb456fe3c15a07692a70723fda1ad135f
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        }
678
679        if( indicator == BluetoothMapAppParams.STATUS_INDICATOR_DELETED) {
680            if (!mObserver.setMessageStatusDeleted(handle, msgType, mCurrentFolder,
681                    mBaseUriString, value)) {
682                if(D) Log.w(TAG,"setMessageStatusDeleted failed");
683                return ResponseCodes.OBEX_HTTP_UNAVAILABLE;
684            }
685        } else if( indicator == BluetoothMapAppParams.STATUS_INDICATOR_READ) {
686            try {
687                if (!mObserver.setMessageStatusRead(handle, msgType, mBaseUriString, value)) {
688                    if(D) Log.w(TAG,"not able to update the message");
689                    return ResponseCodes.OBEX_HTTP_UNAVAILABLE;
690                }
691            } catch (RemoteException e) {
692                if(D) Log.w(TAG,"Error in setMessageStatusRead()", e);
693                return ResponseCodes.OBEX_HTTP_UNAVAILABLE;
694            }
695        }
696        if (extendedData != null) {
697
698        }
699
700        return ResponseCodes.OBEX_HTTP_OK;
701    }
702
703    private int setOwnerStatus(String msgHandle, BluetoothMapAppParams appParams)
704            throws RemoteException{
705        // This does only work for IM
706        if (mAccount != null && mAccount.getType() == BluetoothMapUtils.TYPE.IM) {
707            final Bundle extras = new Bundle(5);
708
709            int presenceState = appParams.getPresenceAvailability();
710            String presenceStatus = appParams.getPresenceStatus();
711            long lastActivity = appParams.getLastActivity();
712            int chatState = appParams.getChatState();
713            String chatStatusConvoId = appParams.getChatStateConvoIdString();
714
715            if(presenceState == BluetoothMapAppParams.INVALID_VALUE_PARAMETER &&
716               presenceStatus == null &&
717               lastActivity == BluetoothMapAppParams.INVALID_VALUE_PARAMETER &&
718               chatState == BluetoothMapAppParams.INVALID_VALUE_PARAMETER &&
719               chatStatusConvoId == null) {
720                return ResponseCodes.OBEX_HTTP_PRECON_FAILED;
721            }
722
723            if(presenceState != BluetoothMapAppParams.INVALID_VALUE_PARAMETER) {
724                extras.putInt(BluetoothMapContract.EXTRA_PRESENCE_STATE, presenceState);
725            }
726            if (presenceStatus != null){
727                extras.putString(BluetoothMapContract.EXTRA_PRESENCE_STATUS, presenceStatus);
728            }
729            if (lastActivity != BluetoothMapAppParams.INVALID_VALUE_PARAMETER){
730                extras.putLong(BluetoothMapContract.EXTRA_LAST_ACTIVE, lastActivity);
731            }
732            if (chatState != BluetoothMapAppParams.INVALID_VALUE_PARAMETER &&
733                chatStatusConvoId != null){
734                extras.putInt(BluetoothMapContract.EXTRA_CHAT_STATE, chatState);
735                extras.putString(BluetoothMapContract.EXTRA_CONVERSATION_ID, chatStatusConvoId);
736            }
737
738            Uri uri = Uri.parse(mBaseUriString);
739            if (D) Log.d(TAG,"setOwnerStatus in: " + uri.toString());
740            try {
741                if (D) Log.d(TAG,"setOwnerStatus call()...");
742                Bundle myBundle = mProviderClient.call(
743                        BluetoothMapContract.METHOD_SET_OWNER_STATUS, null, extras);
744                if (myBundle != null)
745                    return ResponseCodes.OBEX_HTTP_OK;
746                else {
747                    if (D) Log.d(TAG,"setOwnerStatus call failed");
748                    return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
749                }
750            } catch (RemoteException e){
751                mProviderClient = acquireUnstableContentProviderOrThrow();
752                return ResponseCodes.OBEX_HTTP_UNAVAILABLE;
753            } catch (NullPointerException e) {
754                if(D) Log.e(TAG, "setOwnerStatus - if uri or method is null", e);
755                return ResponseCodes.OBEX_HTTP_UNAVAILABLE;
756            } catch (IllegalArgumentException e) {
757                if(D) Log.e(TAG, "setOwnerStatus - if uri is not known", e);
758                return ResponseCodes.OBEX_HTTP_UNAVAILABLE;
759            }
760        }
761        return ResponseCodes.OBEX_HTTP_UNAVAILABLE;
762    }
763
764    @Override
765    public int onSetPath(final HeaderSet request, final HeaderSet reply, final boolean backup,
766            final boolean create) {
767        String folderName;
768        BluetoothMapFolderElement folder;
769        notifyUpdateWakeLock();
770        try {
771            folderName = (String)request.getHeader(HeaderSet.NAME);
772        } catch (Exception e) {
773            if(D) {
774                Log.e(TAG, "request headers error" , e);
775            } else {
776                Log.e(TAG, "request headers error");
777            }
778            return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
779        }
780
781        if (V) logHeader(request);
782        if (D) Log.d(TAG, "onSetPath name is " + folderName +
783                          " backup: " + backup +
784                          " create: " + create);
785
786        if(backup == true){
787            if(mCurrentFolder.getParent() != null)
788                mCurrentFolder = mCurrentFolder.getParent();
789            else
790                return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
791        }
792
793        if (folderName == null || folderName.trim().isEmpty()) {
794            if(backup == false)
795                mCurrentFolder = mCurrentFolder.getRoot();
796        }
797        else {
798            folder = mCurrentFolder.getSubFolder(folderName);
799            if(folder != null)
800                mCurrentFolder = folder;
801            else
802                return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
803        }
804        if (V) Log.d(TAG, "Current Folder: " + mCurrentFolder.getName());
805        return ResponseCodes.OBEX_HTTP_OK;
806    }
807
808    @Override
809    public void onClose() {
810        if (mCallback != null) {
811            Message msg = Message.obtain(mCallback);
812            msg.what = BluetoothMapService.MSG_SERVERSESSION_CLOSE;
813            msg.arg1 = mMasId;
814            msg.sendToTarget();
815            if (D) Log.d(TAG, "onClose(): msg MSG_SERVERSESSION_CLOSE sent out.");
816
817        }
818        if(mProviderClient != null){
819            mProviderClient.release();
820            mProviderClient = null;
821        }
822
823    }
824
825    @Override
826    public int onGet(Operation op) {
827        notifyUpdateWakeLock();
828        mIsAborted = false;
829        HeaderSet request;
830        String type;
831        String name;
832        byte[] appParamRaw = null;
833        BluetoothMapAppParams appParams = null;
834        try {
835            request = op.getReceivedHeader();
836            type = (String)request.getHeader(HeaderSet.TYPE);
837
838            appParamRaw = (byte[])request.getHeader(HeaderSet.APPLICATION_PARAMETER);
839            if(appParamRaw != null)
840                appParams = new BluetoothMapAppParams(appParamRaw);
841
842            if (V) logHeader(request);
843            if (D) Log.d(TAG, "OnGet type is " + type );
844
845            if (type == null) {
846                if (V) Log.d(TAG, "type is null?" + type);
847                return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
848            }
849
850            if (type.equals(TYPE_GET_FOLDER_LISTING)) {
851                if (V && appParams != null) {
852                    Log.d(TAG,"TYPE_GET_FOLDER_LISTING: MaxListCount = "
853                            + appParams.getMaxListCount()
854                            + ", ListStartOffset = " + appParams.getStartOffset());
855                }
856                // Block until all packets have been send.
857                return sendFolderListingRsp(op, appParams);
858            } else if (type.equals(TYPE_GET_MESSAGE_LISTING)){
859                name = (String)request.getHeader(HeaderSet.NAME);
860                if (V && appParams != null) {
861                    Log.d(TAG,"TYPE_GET_MESSAGE_LISTING: folder name is: " + name +
862                            ", MaxListCount = " + appParams.getMaxListCount() +
863                            ", ListStartOffset = " + appParams.getStartOffset());
864                    Log.d(TAG,"SubjectLength = " + appParams.getSubjectLength() +
865                            ", ParameterMask = " + appParams.getParameterMask());
866                    Log.d(TAG,"FilterMessageType = " + appParams.getFilterMessageType() );
867                    Log.d(TAG,"FilterPeriodBegin = " + appParams.getFilterPeriodBeginString() +
868                            ", FilterPeriodEnd = " + appParams.getFilterPeriodEndString() +
869                            ", FilterReadStatus = " + appParams.getFilterReadStatus());
870                    Log.d(TAG,"FilterRecipient = " + appParams.getFilterRecipient() +
871                            ", FilterOriginator = " + appParams.getFilterOriginator());
872                    Log.d(TAG,"FilterPriority = " + appParams.getFilterPriority());
873                    long tmpLong = appParams.getFilterMsgHandle();
874                    Log.d(TAG,"FilterMsgHandle = " + (
875                            (tmpLong == BluetoothMapAppParams.INVALID_VALUE_PARAMETER) ? "" :
876                                Long.toHexString(tmpLong)));
877                    SignedLongLong tmpLongLong = appParams.getFilterConvoId();
878                    Log.d(TAG,"FilterConvoId = " + ((tmpLongLong == null) ? "" :
879                        Long.toHexString(tmpLongLong.getLeastSignificantBits()) ) );
880                }
881                // Block until all packets have been send.
882                return sendMessageListingRsp(op, appParams, name);
883
884            } else if (type.equals(TYPE_GET_CONVO_LISTING)){
885                name = (String)request.getHeader(HeaderSet.NAME);
886                if (V && appParams != null) {
887                    Log.d(TAG,"TYPE_GET_CONVO_LISTING: name is" + name +
888                            ", MaxListCount = " + appParams.getMaxListCount() +
889                            ", ListStartOffset = " + appParams.getStartOffset());
890                    Log.d(TAG,"FilterLastActivityBegin = "+appParams.getFilterLastActivityBegin());
891                    Log.d(TAG,"FilterLastActivityEnd = " + appParams.getFilterLastActivityEnd());
892                    Log.d(TAG,"FilterReadStatus = " + appParams.getFilterReadStatus());
893                    Log.d(TAG,"FilterRecipient = " + appParams.getFilterRecipient());
894                }
895                // Block until all packets have been send.
896                return sendConvoListingRsp(op, appParams,name);
897            } else if (type.equals(TYPE_GET_MAS_INSTANCE_INFORMATION)) {
898                if(V && appParams != null) {
899                    Log.d(TAG,"TYPE_MESSAGE (GET): MASInstandeId = "
900                            + appParams.getMasInstanceId());
901                }
902                // Block until all packets have been send.
903                return sendMASInstanceInformationRsp(op, appParams);
904            } else if (type.equals(TYPE_MESSAGE)){
905                name = (String)request.getHeader(HeaderSet.NAME);
906                if(V && appParams != null) {
907                    Log.d(TAG,"TYPE_MESSAGE (GET): name is" + name +
908                            ", Attachment = " + appParams.getAttachment() +
909                            ", Charset = " + appParams.getCharset() +
910                            ", FractionRequest = " + appParams.getFractionRequest());
911                }
912                // Block until all packets have been send.
913                return sendGetMessageRsp(op, name, appParams, mMessageVersion);
914            } else {
915                Log.w(TAG, "unknown type request: " + type);
916                return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE;
917            }
918
919        } catch (IllegalArgumentException e) {
920            Log.e(TAG, "Exception:", e);
921            return ResponseCodes.OBEX_HTTP_PRECON_FAILED;
922        } catch (ParseException e) {
923            Log.e(TAG, "Exception:", e);
924            return ResponseCodes.OBEX_HTTP_PRECON_FAILED;
925        } catch (Exception e) {
926            if(D) {
927                Log.e(TAG, "Exception occured while handling request",e);
928            } else {
929                Log.e(TAG, "Exception occured while handling request");
930            }
931            if(mIsAborted == true) {
932                if(D) Log.d(TAG, "onGet Operation Aborted");
933                return ResponseCodes.OBEX_HTTP_OK;
934            } else {
935                return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
936            }
937        }
938    }
939
940    /**
941     * Generate and send the message listing response based on an application
942     * parameter header. This function call will block until complete or aborted
943     * by the peer. Fragmentation of packets larger than the obex packet size
944     * will be handled by this function.
945     *
946     * @param op
947     *            The OBEX operation.
948     * @param appParams
949     *            The application parameter header
950     * @return {@link ResponseCodes.OBEX_HTTP_OK} on success or
951     *         {@link ResponseCodes.OBEX_HTTP_BAD_REQUEST} on error.
952     */
953    private int sendMessageListingRsp(Operation op,
954                                      BluetoothMapAppParams appParams,
955                                      String folderName){
956        OutputStream outStream = null;
957        byte[] outBytes = null;
958        int maxChunkSize, bytesToWrite, bytesWritten = 0, listSize;
959        boolean hasUnread = false;
960        HeaderSet replyHeaders = new HeaderSet();
961        BluetoothMapAppParams outAppParams = new BluetoothMapAppParams();
962        BluetoothMapMessageListing outList;
963        if(appParams == null){
964            appParams = new BluetoothMapAppParams();
965            appParams.setMaxListCount(1024);
966            appParams.setStartOffset(0);
967        }
968
969        /* MAP Spec 1.3 introduces the following
970         * Messagehandle filtering:
971         * msgListing (messageHandle=X) -> other allowed filters: parametereMask, subjectMaxLength
972         * ConversationID filtering:
973         * msgListing (convoId empty) -> should work as normal msgListing in valid folders
974         * msgListing (convoId=0, no other filters) -> should return all messages in all folders
975         * msgListing (convoId=N, other filters) -> should return all messages in conversationID=N
976         *                                          according to filters requested
977         */
978        BluetoothMapFolderElement folderToList = null;
979        if (appParams.getFilterMsgHandle() != BluetoothMapAppParams.INVALID_VALUE_PARAMETER
980            || appParams.getFilterConvoId() != null) {
981            // If messageHandle or convoId filtering ignore folder
982            Log.v(TAG,"sendMessageListingRsp: ignore folder ");
983            folderToList = mCurrentFolder.getRoot();
984            folderToList.setIngore(true);
985        } else {
986            folderToList = getFolderElementFromName(folderName);
987            if(folderToList == null) {
988                Log.w(TAG,"sendMessageListingRsp: folderToList == "+
989                        "null-sending OBEX_HTTP_BAD_REQUEST");
990                return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
991            }
992            Log.v(TAG,"sendMessageListingRsp: has sms " + folderToList.hasSmsMmsContent() +
993                    "has email " + folderToList.hasEmailContent() +
994                    "has IM " + folderToList.hasImContent() );
995        }
996
997        try {
998            // Open the OBEX body stream
999            outStream = op.openOutputStream();
1000
1001            if(appParams.getMaxListCount() == BluetoothMapAppParams.INVALID_VALUE_PARAMETER)
1002                appParams.setMaxListCount(1024);
1003
1004            if(appParams.getStartOffset() == BluetoothMapAppParams.INVALID_VALUE_PARAMETER)
1005                appParams.setStartOffset(0);
1006
1007            // Check to see if we only need to send the size - hence no need to encode.
1008            if(appParams.getMaxListCount() != 0) {
1009                outList = mOutContent.msgListing(folderToList, appParams);
1010                // Generate the byte stream
1011                outAppParams.setMessageListingSize(outList.getCount());
1012                String version;
1013                if(0 < (mRemoteFeatureMask &
1014                        BluetoothMapUtils.MAP_FEATURE_MESSAGE_LISTING_FORMAT_V11_BIT)) {
1015                    version = BluetoothMapUtils.MAP_V11_STR;
1016                } else {
1017                    version = BluetoothMapUtils.MAP_V10_STR;
1018                }
1019                /* This will only set the version, the bit must also be checked before adding any
1020                 * 1.1 bits to the listing. */
1021                outBytes = outList.encode(mThreadIdSupport, version);
1022                hasUnread = outList.hasUnread();
1023            } else {
1024                listSize = mOutContent.msgListingSize(folderToList, appParams);
1025                hasUnread = mOutContent.msgListingHasUnread(folderToList, appParams);
1026                outAppParams.setMessageListingSize(listSize);
1027                op.noBodyHeader();
1028            }
1029            folderToList.setIngore(false);
1030            // Build the application parameter header
1031            // let the peer know if there are unread messages in the list
1032            if(hasUnread) {
1033                outAppParams.setNewMessage(1);
1034            }else{
1035                outAppParams.setNewMessage(0);
1036            }
1037            if ((mRemoteFeatureMask & BluetoothMapUtils.MAP_FEATURE_DATABASE_INDENTIFIER_BIT)
1038                    == BluetoothMapUtils.MAP_FEATURE_DATABASE_INDENTIFIER_BIT ) {
1039                outAppParams.setDatabaseIdentifier(0, mMasInstance.getDbIdentifier());
1040            }
1041            if((mRemoteFeatureMask & BluetoothMapUtils.MAP_FEATURE_FOLDER_VERSION_COUNTER_BIT)
1042                    == BluetoothMapUtils.MAP_FEATURE_FOLDER_VERSION_COUNTER_BIT) {
1043                // Force update of version counter if needed
1044                mObserver.refreshFolderVersionCounter();
1045                outAppParams.setFolderVerCounter(mMasInstance.getFolderVersionCounter(), 0);
1046            }
1047            outAppParams.setMseTime(Calendar.getInstance().getTime().getTime());
1048            replyHeaders.setHeader(HeaderSet.APPLICATION_PARAMETER, outAppParams.EncodeParams());
1049            op.sendHeaders(replyHeaders);
1050
1051        } catch (IOException e) {
1052            Log.w(TAG,"sendMessageListingRsp: IOException - sending OBEX_HTTP_BAD_REQUEST", e);
1053            if(outStream != null) { try { outStream.close(); } catch (IOException ex) {} }
1054            if(mIsAborted == true) {
1055                if(D) Log.d(TAG, "sendMessageListingRsp Operation Aborted");
1056                return ResponseCodes.OBEX_HTTP_OK;
1057            } else {
1058                return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
1059            }
1060        } catch (IllegalArgumentException e) {
1061            Log.w(TAG,"sendMessageListingRsp: IllegalArgumentException"+
1062                                            " - sending OBEX_HTTP_BAD_REQUEST", e);
1063            if(outStream != null) { try { outStream.close(); } catch (IOException ex) {} }
1064            return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
1065        }
1066
1067        maxChunkSize = op.getMaxPacketSize(); // This must be called after setting the headers.
1068        if(outBytes != null) {
1069            try {
1070                while (bytesWritten < outBytes.length && mIsAborted == false) {
1071                    bytesToWrite = Math.min(maxChunkSize, outBytes.length - bytesWritten);
1072                    outStream.write(outBytes, bytesWritten, bytesToWrite);
1073                    bytesWritten += bytesToWrite;
1074                }
1075            } catch (IOException e) {
1076                if(D) Log.w(TAG,e);
1077                // We were probably aborted or disconnected
1078            } finally {
1079                if(outStream != null) { try { outStream.close(); } catch (IOException e) {} }
1080            }
1081            if(bytesWritten != outBytes.length && !mIsAborted) {
1082                Log.w(TAG,"sendMessageListingRsp: bytesWritten != outBytes.length" +
1083                        " - sending OBEX_HTTP_BAD_REQUEST");
1084                return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
1085            }
1086        } else {
1087            if(outStream != null) { try { outStream.close(); } catch (IOException e) {} }
1088        }
1089        return ResponseCodes.OBEX_HTTP_OK;
1090    }
1091
1092    /**
1093     * Update the {@link BluetoothMapAppParams} object message type filter mask to only contain
1094     * message types supported by this mas instance.
1095     * Could the folder be used in stead?
1096     * @param appParams Reference to the object to update
1097     * @param overwrite True: The msgType will be overwritten to match the message types supported
1098     * by this MAS instance. False: any unsupported message types will be masked out.
1099     */
1100    private void setMsgTypeFilterParams(BluetoothMapAppParams appParams, boolean overwrite) {
1101        int masFilterMask = 0;
1102        if(!mEnableSmsMms) {
1103            masFilterMask |= BluetoothMapAppParams.FILTER_NO_SMS_CDMA;
1104            masFilterMask |= BluetoothMapAppParams.FILTER_NO_SMS_GSM;
1105            masFilterMask |= BluetoothMapAppParams.FILTER_NO_MMS;
1106        }
1107        if(mAccount==null){
1108            masFilterMask |= BluetoothMapAppParams.FILTER_NO_EMAIL;
1109            masFilterMask |= BluetoothMapAppParams.FILTER_NO_IM;
1110        } else {
1111            if(!(mAccount.getType() == BluetoothMapUtils.TYPE.EMAIL)) {
1112                masFilterMask |= BluetoothMapAppParams.FILTER_NO_EMAIL;
1113            }
1114            if(!(mAccount.getType() == BluetoothMapUtils.TYPE.IM)) {
1115                masFilterMask |= BluetoothMapAppParams.FILTER_NO_IM;
1116            }
1117        }
1118        if(overwrite) {
1119            appParams.setFilterMessageType(masFilterMask);
1120        } else {
1121            int newMask = appParams.getFilterMessageType();
1122            if(newMask == BluetoothMapAppParams.INVALID_VALUE_PARAMETER) {
1123                appParams.setFilterMessageType(newMask);
1124            } else {
1125                newMask |= masFilterMask;
1126                appParams.setFilterMessageType(newMask);
1127            }
1128        }
1129    }
1130
1131    /**
1132     * Generate and send the Conversation listing response based on an application
1133     * parameter header. This function call will block until complete or aborted
1134     * by the peer. Fragmentation of packets larger than the obex packet size
1135     * will be handled by this function.
1136     *
1137     * @param op
1138     *            The OBEX operation.
1139     * @param appParams
1140     *            The application parameter header
1141     * @return {@link ResponseCodes.OBEX_HTTP_OK} on success or
1142     *         {@link ResponseCodes.OBEX_HTTP_BAD_REQUEST} on error.
1143     */
1144    private int sendConvoListingRsp(Operation op,
1145                                    BluetoothMapAppParams appParams,
1146                                    String folderName){
1147        OutputStream outStream = null;
1148        byte[] outBytes = null;
1149        int maxChunkSize, bytesToWrite, bytesWritten = 0;
1150        //boolean hasUnread = false;
1151        HeaderSet replyHeaders = new HeaderSet();
1152        BluetoothMapAppParams outAppParams = new BluetoothMapAppParams();
1153        BluetoothMapConvoListing outList;
1154        if(appParams == null){
1155            appParams = new BluetoothMapAppParams();
1156            appParams.setMaxListCount(1024);
1157            appParams.setStartOffset(0);
1158        }
1159        // As the app parameters do not carry which message types to list, we set the filter here
1160        // to all message types supported by this instance.
1161        setMsgTypeFilterParams(appParams, true);
1162
1163        // Check to see if we only need to send the size - hence no need to encode.
1164        try {
1165            // Open the OBEX body stream
1166            outStream = op.openOutputStream();
1167
1168            if(appParams.getMaxListCount() == BluetoothMapAppParams.INVALID_VALUE_PARAMETER)
1169                appParams.setMaxListCount(1024);
1170
1171            if(appParams.getStartOffset() == BluetoothMapAppParams.INVALID_VALUE_PARAMETER)
1172                appParams.setStartOffset(0);
1173
1174            if(appParams.getMaxListCount() != 0) {
1175                outList = mOutContent.convoListing(appParams, false);
1176                outAppParams.setConvoListingSize(outList.getCount());
1177                // Generate the byte stream
1178                outBytes = outList.encode(); // Include thread ID for clients that supports it.
1179      //          hasUnread = outList.hasUnread();
1180                if(D) Log.d(TAG, "outBytes size:"+ outBytes.length);
1181            } else {
1182                outList = mOutContent.convoListing(appParams, true);
1183                outAppParams.setConvoListingSize(outList.getCount());
1184                if(mEnableSmsMms) {
1185                    mOutContent.refreshSmsMmsConvoVersions();
1186                }
1187                if(mAccount != null) {
1188                    mOutContent.refreshImEmailConvoVersions();
1189                }
1190                // Force update of version counter if needed
1191                mObserver.refreshConvoListVersionCounter();
1192                if(0 < (mRemoteFeatureMask &
1193                        BluetoothMapUtils.MAP_FEATURE_CONVERSATION_VERSION_COUNTER_BIT)) {
1194                    outAppParams.setConvoListingVerCounter(
1195                            mMasInstance.getCombinedConvoListVersionCounter(), 0);
1196                }
1197                op.noBodyHeader();
1198            }
1199            if(D) Log.d(TAG, "outList size:"+ outList.getCount()
1200                    + " MaxListCount: "+appParams.getMaxListCount());
1201            outList = null; // We don't need it anymore - we might as well give it up for GC
1202            outAppParams.setDatabaseIdentifier(0, mMasInstance.getDbIdentifier());
1203
1204            // Build the application parameter header
1205            // The MseTime is not in the CR - but I think it is missing.
1206            outAppParams.setMseTime(Calendar.getInstance().getTime().getTime());
1207            replyHeaders.setHeader(HeaderSet.APPLICATION_PARAMETER, outAppParams.EncodeParams());
1208            op.sendHeaders(replyHeaders);
1209
1210        } catch (IOException e) {
1211            Log.w(TAG,"sendConvoListingRsp: IOException - sending OBEX_HTTP_BAD_REQUEST", e);
1212            if(outStream != null) { try { outStream.close(); } catch (IOException ex) {} }
1213            if(mIsAborted == true) {
1214                if(D) Log.d(TAG, "sendConvoListingRsp Operation Aborted");
1215                return ResponseCodes.OBEX_HTTP_OK;
1216            } else {
1217                return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
1218            }
1219        } catch (IllegalArgumentException e) {
1220            Log.w(TAG,"sendConvoListingRsp: IllegalArgumentException" +
1221                    " - sending OBEX_HTTP_BAD_REQUEST", e);
1222            if(outStream != null) { try { outStream.close(); } catch (IOException ex) {} }
1223            return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
1224        }
1225
1226        maxChunkSize = op.getMaxPacketSize(); // This must be called after setting the headers.
1227        if(outBytes != null) {
1228            try {
1229                while (bytesWritten < outBytes.length && mIsAborted == false) {
1230                    bytesToWrite = Math.min(maxChunkSize, outBytes.length - bytesWritten);
1231                    outStream.write(outBytes, bytesWritten, bytesToWrite);
1232                    bytesWritten += bytesToWrite;
1233                }
1234            } catch (IOException e) {
1235                if(D) Log.w(TAG,e);
1236                // We were probably aborted or disconnected
1237            } finally {
1238                if(outStream != null) { try { outStream.close(); } catch (IOException e) {} }
1239            }
1240            if(bytesWritten != outBytes.length && !mIsAborted) {
1241                Log.w(TAG,"sendConvoListingRsp: bytesWritten != outBytes.length" +
1242                        " - sending OBEX_HTTP_BAD_REQUEST");
1243                return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
1244            }
1245        } else {
1246            if(outStream != null) { try { outStream.close(); } catch (IOException e) {} }
1247        }
1248        return ResponseCodes.OBEX_HTTP_OK;
1249    }
1250
1251    /**
1252     * Generate and send the Folder listing response based on an application
1253     * parameter header. This function call will block until complete or aborted
1254     * by the peer. Fragmentation of packets larger than the obex packet size
1255     * will be handled by this function.
1256     *
1257     * @param op
1258     *            The OBEX operation.
1259     * @param appParams
1260     *            The application parameter header
1261     * @return {@link ResponseCodes.OBEX_HTTP_OK} on success or
1262     *         {@link ResponseCodes.OBEX_HTTP_BAD_REQUEST} on error.
1263     */
1264    private int sendFolderListingRsp(Operation op, BluetoothMapAppParams appParams){
1265        OutputStream outStream = null;
1266        byte[] outBytes = null;
1267        BluetoothMapAppParams outAppParams = new BluetoothMapAppParams();
1268        int maxChunkSize, bytesWritten = 0;
1269        HeaderSet replyHeaders = new HeaderSet();
1270        int bytesToWrite, maxListCount, listStartOffset;
1271        if(appParams == null){
1272            appParams = new BluetoothMapAppParams();
1273            appParams.setMaxListCount(1024);
1274        }
1275
1276        if(V)
1277            Log.v(TAG,"sendFolderList for " + mCurrentFolder.getName());
1278
1279        try {
1280            maxListCount = appParams.getMaxListCount();
1281            listStartOffset = appParams.getStartOffset();
1282
1283            if(listStartOffset == BluetoothMapAppParams.INVALID_VALUE_PARAMETER)
1284                listStartOffset = 0;
1285
1286            if(maxListCount == BluetoothMapAppParams.INVALID_VALUE_PARAMETER)
1287                maxListCount = 1024;
1288
1289            if(maxListCount != 0)
1290            {
1291                outBytes = mCurrentFolder.encode(listStartOffset, maxListCount);
1292                outStream = op.openOutputStream();
1293            } else {
1294                // ESR08 specified that this shall only be included for MaxListCount=0
1295                outAppParams.setFolderListingSize(mCurrentFolder.getSubFolderCount());
1296                op.noBodyHeader();
1297            }
1298
1299            // Build and set the application parameter header
1300            replyHeaders.setHeader(HeaderSet.APPLICATION_PARAMETER, outAppParams.EncodeParams());
1301            op.sendHeaders(replyHeaders);
1302
1303        } catch (IOException e1) {
1304            Log.w(TAG,"sendFolderListingRsp: IOException" +
1305                    " - sending OBEX_HTTP_BAD_REQUEST Exception:", e1);
1306            if(outStream != null) { try { outStream.close(); } catch (IOException e) {} }
1307            if(mIsAborted == true) {
1308                if(D) Log.d(TAG, "sendFolderListingRsp Operation Aborted");
1309                return ResponseCodes.OBEX_HTTP_OK;
1310            } else {
1311                return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
1312            }
1313        } catch (IllegalArgumentException e1) {
1314            Log.w(TAG,"sendFolderListingRsp: IllegalArgumentException" +
1315                    " - sending OBEX_HTTP_BAD_REQUEST Exception:", e1);
1316            if(outStream != null) { try { outStream.close(); } catch (IOException e) {} }
1317            return ResponseCodes.OBEX_HTTP_PRECON_FAILED;
1318        }
1319
1320        maxChunkSize = op.getMaxPacketSize(); // This must be called after setting the headers.
1321
1322        if(outBytes != null) {
1323            try {
1324                while (bytesWritten < outBytes.length && mIsAborted == false) {
1325                    bytesToWrite = Math.min(maxChunkSize, outBytes.length - bytesWritten);
1326                    outStream.write(outBytes, bytesWritten, bytesToWrite);
1327                    bytesWritten += bytesToWrite;
1328                }
1329            } catch (IOException e) {
1330                // We were probably aborted or disconnected
1331            } finally {
1332                if(outStream != null) { try { outStream.close(); } catch (IOException e) {} }
1333            }
1334            if(V)
1335                Log.v(TAG,"sendFolderList sent " + bytesWritten+" bytes out of "+ outBytes.length);
1336            if(bytesWritten == outBytes.length || mIsAborted)
1337                return ResponseCodes.OBEX_HTTP_OK;
1338            else
1339                return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
1340        }
1341
1342        return ResponseCodes.OBEX_HTTP_OK;
1343    }
1344
1345    /**
1346     * Generate and send the get MAS Instance Information response based on an MAS Instance
1347     *
1348     * @param op
1349     *            The OBEX operation.
1350     * @param appParams
1351     *            The application parameter header
1352     * @return {@link ResponseCodes.OBEX_HTTP_OK} on success or
1353     *         {@link ResponseCodes.OBEX_HTTP_BAD_REQUEST} on error.
1354     */
1355    private int sendMASInstanceInformationRsp(Operation op, BluetoothMapAppParams appParams){
1356
1357        OutputStream outStream = null;
1358        byte[] outBytes = null;
1359        String outString = null;
1360        int maxChunkSize, bytesToWrite, bytesWritten = 0;
1361
1362        try {
1363            if(mMasId == appParams.getMasInstanceId()) {
1364                if(mAccount != null) {
1365                    if(mAccount.getType() == TYPE.EMAIL) {
1366                        outString = (mAccount.getName() != null) ? mAccount.getName() :
1367                            BluetoothMapMasInstance.TYPE_EMAIL_STR;
1368                    } else if(mAccount.getType() == TYPE.IM){
1369                        outString = mAccount.getUciFull();
1370                        if(outString == null) {
1371                            String uci = mAccount.getUci();
1372                            // TODO: Do we need to align this with HF/PBAP
1373                            StringBuilder sb =
1374                                    new StringBuilder(uci == null ? 5 : 5 + uci.length());
1375                            sb.append("un");
1376                            if(mMasId < 10) {
1377                                sb.append("00");
1378                            } else if(mMasId < 100) {
1379                                sb.append("0");
1380                            }
1381                            sb.append(mMasId);
1382                            if(uci != null) {
1383                                sb.append(":").append(uci);
1384                            }
1385                            outString = sb.toString();
1386                        }
1387                    }
1388                } else {
1389                    outString = BluetoothMapMasInstance.TYPE_SMS_MMS_STR;
1390                    // TODO: Add phone number if possible
1391                }
1392            } else {
1393                return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
1394            }
1395
1396            /* Ensure byte array max length is 200 containing valid UTF-8 characters */
1397            outBytes = BluetoothMapUtils.truncateUtf8StringToBytearray(outString,
1398                    MAS_INSTANCE_INFORMATION_LENGTH);
1399
1400            // Open the OBEX body stream
1401            outStream = op.openOutputStream();
1402
1403        } catch (IOException e) {
1404            Log.w(TAG,"sendMASInstanceInformationRsp: IOException" +
1405                    " - sending OBEX_HTTP_BAD_REQUEST", e);
1406            if(mIsAborted == true) {
1407                if(D) Log.d(TAG, "sendMASInstanceInformationRsp Operation Aborted");
1408                return ResponseCodes.OBEX_HTTP_OK;
1409            } else {
1410                return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
1411            }
1412        }
1413
1414        maxChunkSize = op.getMaxPacketSize(); // This must be called after setting the headers.
1415
1416        if(outBytes != null) {
1417            try {
1418                while (bytesWritten < outBytes.length && mIsAborted == false) {
1419                    bytesToWrite = Math.min(maxChunkSize, outBytes.length - bytesWritten);
1420                    outStream.write(outBytes, bytesWritten, bytesToWrite);
1421                    bytesWritten += bytesToWrite;
1422                }
1423            } catch (IOException e) {
1424                // We were probably aborted or disconnected
1425            } finally {
1426                if(outStream != null) { try { outStream.close(); } catch (IOException e) {} }
1427            }
1428            if(V)
1429                Log.v(TAG,"sendMASInstanceInformationRsp sent " + bytesWritten +
1430                        " bytes out of "+ outBytes.length);
1431            if(bytesWritten == outBytes.length || mIsAborted)
1432                return ResponseCodes.OBEX_HTTP_OK;
1433            else
1434                return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
1435        }
1436        return ResponseCodes.OBEX_HTTP_OK;
1437    }
1438
1439    /**
1440     * Generate and send the get message response based on an application
1441     * parameter header and a handle.
1442     *
1443     * @param op
1444     *            The OBEX operation.
1445     * @param handle
1446     *            The handle of the requested message
1447     * @param appParams
1448     *            The application parameter header
1449     * @param version
1450     *              The string representation of the version number(i.e. "1.0")
1451     * @return {@link ResponseCodes.OBEX_HTTP_OK} on success or
1452     *         {@link ResponseCodes.OBEX_HTTP_BAD_REQUEST} on error.
1453     */
1454    private int sendGetMessageRsp(Operation op, String handle,
1455            BluetoothMapAppParams appParams, String version){
1456        OutputStream outStream = null;
1457        byte[] outBytes = null;
1458        int maxChunkSize, bytesToWrite, bytesWritten = 0;
1459
1460        try {
1461            outBytes = mOutContent.getMessage(handle, appParams, mCurrentFolder, version);
1462            outStream = op.openOutputStream();
1463
1464            // If it is a fraction request of Email message, set header before responding
1465            if ((BluetoothMapUtils.getMsgTypeFromHandle(handle).equals(TYPE.EMAIL)||
1466                    (BluetoothMapUtils.getMsgTypeFromHandle(handle).equals(TYPE.IM))) &&
1467                    (appParams.getFractionRequest() ==
1468                    BluetoothMapAppParams.FRACTION_REQUEST_FIRST)) {
1469                BluetoothMapAppParams outAppParams  = new BluetoothMapAppParams();
1470                HeaderSet replyHeaders = new HeaderSet();
1471                outAppParams.setFractionDeliver(BluetoothMapAppParams.FRACTION_DELIVER_LAST);
1472                // Build and set the application parameter header
1473                replyHeaders.setHeader(HeaderSet.APPLICATION_PARAMETER,
1474                        outAppParams.EncodeParams());
1475                op.sendHeaders(replyHeaders);
1476                if(V) Log.v(TAG,"sendGetMessageRsp fractionRequest - " +
1477                        "set FRACTION_DELIVER_LAST header");
1478            }
1479
1480        } catch (IOException e) {
1481            Log.w(TAG,"sendGetMessageRsp: IOException - sending OBEX_HTTP_BAD_REQUEST", e);
1482            if(outStream != null) { try { outStream.close(); } catch (IOException ex) {} }
1483            if(mIsAborted == true) {
1484                if(D) Log.d(TAG, "sendGetMessageRsp Operation Aborted");
1485                return ResponseCodes.OBEX_HTTP_OK;
1486            } else {
1487                return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
1488            }
1489        } catch (IllegalArgumentException e) {
1490            Log.w(TAG,"sendGetMessageRsp: IllegalArgumentException (e.g. invalid handle) - " +
1491                    "sending OBEX_HTTP_BAD_REQUEST", e);
1492            if(outStream != null) { try { outStream.close(); } catch (IOException ex) {} }
1493            return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
1494        }
1495
1496        maxChunkSize = op.getMaxPacketSize(); // This must be called after setting the headers.
1497
1498        if(outBytes != null) {
1499            try {
1500                while (bytesWritten < outBytes.length && mIsAborted == false) {
1501                    bytesToWrite = Math.min(maxChunkSize, outBytes.length - bytesWritten);
1502                    outStream.write(outBytes, bytesWritten, bytesToWrite);
1503                    bytesWritten += bytesToWrite;
1504                }
1505            } catch (IOException e) {
1506                // We were probably aborted or disconnected
1507                if(D && e.getMessage().equals("Abort Received")) {
1508                    Log.w(TAG, "getMessage() Aborted...", e);
1509                }
1510            } finally {
1511                if(outStream != null) { try { outStream.close(); } catch (IOException e) {} }
1512            }
1513            if(bytesWritten == outBytes.length || mIsAborted)
1514                return ResponseCodes.OBEX_HTTP_OK;
1515            else
1516                return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
1517        }
1518
1519        return ResponseCodes.OBEX_HTTP_OK;
1520    }
1521
1522    @Override
1523    public int onDelete(HeaderSet request, HeaderSet reply) {
1524        if(D) Log.v(TAG, "onDelete() " + request.toString());
1525        mIsAborted = false;
1526        notifyUpdateWakeLock();
1527        String type, name;
1528        byte[] appParamRaw;
1529        BluetoothMapAppParams appParams = null;
1530
1531        /* TODO: If this is to be placed here, we need to cleanup - e.g. the exception handling */
1532        try {
1533            type = (String)request.getHeader(HeaderSet.TYPE);
1534
1535            name = (String)request.getHeader(HeaderSet.NAME);
1536            appParamRaw = (byte[])request.getHeader(HeaderSet.APPLICATION_PARAMETER);
1537            if(appParamRaw != null)
1538                appParams = new BluetoothMapAppParams(appParamRaw);
1539            if(D) Log.d(TAG,"type = " + type + ", name = " + name);
1540            if(type.equals(TYPE_SET_NOTIFICATION_FILTER)) {
1541                if(V) {
1542                    Log.d(TAG,"TYPE_SET_NOTIFICATION_FILTER: NotificationFilter: "
1543                            + appParams.getNotificationFilter());
1544                }
1545                mObserver.setNotificationFilter(appParams.getNotificationFilter());
1546                return ResponseCodes.OBEX_HTTP_OK;
1547            }  else if (type.equals(TYPE_SET_OWNER_STATUS)) {
1548                if(V) {
1549                    Log.d(TAG,"TYPE_SET_OWNER_STATUS:" +
1550                          " PresenceAvailability " + appParams.getPresenceAvailability() +
1551                          ", PresenceStatus: " + appParams.getPresenceStatus() +
1552                          ", LastActivity: " + appParams.getLastActivityString() +
1553                          ", ChatStatus: " + appParams.getChatState() +
1554                          ", ChatStatusConvoId: " + appParams.getChatStateConvoIdString());
1555                }
1556                return setOwnerStatus(name, appParams);
1557            }
1558
1559        } catch (RemoteException e){
1560            //reload the providerClient and return error
1561            try {
1562                mProviderClient = acquireUnstableContentProviderOrThrow();
1563            }catch (RemoteException e2){
1564                //should not happen
1565            }
1566            return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
1567        }catch (Exception e) {
1568
1569            if(D) {
1570                Log.e(TAG, "Exception occured while handling request",e);
1571            } else {
1572                Log.e(TAG, "Exception occured while handling request");
1573            }
1574            if(mIsAborted) {
1575                return ResponseCodes.OBEX_HTTP_OK;
1576            } else {
1577                return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
1578            }
1579        }
1580        return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
1581    }
1582
1583    private void notifyUpdateWakeLock() {
1584        if(mCallback != null) {
1585            Message msg = Message.obtain(mCallback);
1586            msg.what = BluetoothMapService.MSG_ACQUIRE_WAKE_LOCK;
1587            msg.sendToTarget();
1588        }
1589    }
1590
1591    private static final void logHeader(HeaderSet hs) {
1592        Log.v(TAG, "Dumping HeaderSet " + hs.toString());
1593        try {
1594            Log.v(TAG, "CONNECTION_ID : " + hs.getHeader(HeaderSet.CONNECTION_ID));
1595            Log.v(TAG, "NAME : " + hs.getHeader(HeaderSet.NAME));
1596            Log.v(TAG, "TYPE : " + hs.getHeader(HeaderSet.TYPE));
1597            Log.v(TAG, "TARGET : " + hs.getHeader(HeaderSet.TARGET));
1598            Log.v(TAG, "WHO : " + hs.getHeader(HeaderSet.WHO));
1599            Log.v(TAG, "APPLICATION_PARAMETER : " + hs.getHeader(HeaderSet.APPLICATION_PARAMETER));
1600        } catch (IOException e) {
1601            Log.e(TAG, "dump HeaderSet error " + e);
1602        }
1603        Log.v(TAG, "NEW!!! Dumping HeaderSet END");
1604    }
1605}
1606