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