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