BluetoothMapObexServer.java revision db8d8ae565b3db6a5e3187170dcb7b281a79f9da
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 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 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 // hasUnread = outList.hasUnread(); 1213 if(D) Log.d(TAG, "outBytes size:"+ outBytes.length); 1214 } else { 1215 outList = mOutContent.convoListing(appParams, true); 1216 outAppParams.setConvoListingSize(outList.getCount()); 1217 if(mEnableSmsMms) { 1218 mOutContent.refreshSmsMmsConvoVersions(); 1219 } 1220 if(mAccount != null) { 1221 mOutContent.refreshImEmailConvoVersions(); 1222 } 1223 // Force update of version counter if needed 1224 mObserver.refreshConvoListVersionCounter(); 1225 if(0 < (mRemoteFeatureMask & 1226 BluetoothMapUtils.MAP_FEATURE_CONVERSATION_VERSION_COUNTER_BIT)) { 1227 outAppParams.setConvoListingVerCounter( 1228 mMasInstance.getCombinedConvoListVersionCounter(), 0); 1229 } 1230 op.noBodyHeader(); 1231 } 1232 if(D) Log.d(TAG, "outList size:"+ outList.getCount() 1233 + " MaxListCount: "+appParams.getMaxListCount()); 1234 outList = null; // We don't need it anymore - we might as well give it up for GC 1235 outAppParams.setDatabaseIdentifier(0, mMasInstance.getDbIdentifier()); 1236 1237 // Build the application parameter header 1238 // The MseTime is not in the CR - but I think it is missing. 1239 outAppParams.setMseTime(Calendar.getInstance().getTime().getTime()); 1240 replyHeaders.setHeader(HeaderSet.APPLICATION_PARAMETER, outAppParams.EncodeParams()); 1241 op.sendHeaders(replyHeaders); 1242 1243 } catch (IOException e) { 1244 Log.w(TAG,"sendConvoListingRsp: IOException - sending OBEX_HTTP_BAD_REQUEST", e); 1245 if(outStream != null) { try { outStream.close(); } catch (IOException ex) {} } 1246 if(mIsAborted == true) { 1247 if(D) Log.d(TAG, "sendConvoListingRsp Operation Aborted"); 1248 return ResponseCodes.OBEX_HTTP_OK; 1249 } else { 1250 return ResponseCodes.OBEX_HTTP_BAD_REQUEST; 1251 } 1252 } catch (IllegalArgumentException e) { 1253 Log.w(TAG,"sendConvoListingRsp: IllegalArgumentException" + 1254 " - sending OBEX_HTTP_BAD_REQUEST", e); 1255 if(outStream != null) { try { outStream.close(); } catch (IOException ex) {} } 1256 return ResponseCodes.OBEX_HTTP_BAD_REQUEST; 1257 } 1258 1259 maxChunkSize = op.getMaxPacketSize(); // This must be called after setting the headers. 1260 if(outBytes != null) { 1261 try { 1262 while (bytesWritten < outBytes.length && mIsAborted == false) { 1263 bytesToWrite = Math.min(maxChunkSize, outBytes.length - bytesWritten); 1264 outStream.write(outBytes, bytesWritten, bytesToWrite); 1265 bytesWritten += bytesToWrite; 1266 } 1267 } catch (IOException e) { 1268 if(D) Log.w(TAG,e); 1269 // We were probably aborted or disconnected 1270 } finally { 1271 if(outStream != null) { try { outStream.close(); } catch (IOException e) {} } 1272 } 1273 if(bytesWritten != outBytes.length && !mIsAborted) { 1274 Log.w(TAG,"sendConvoListingRsp: bytesWritten != outBytes.length" + 1275 " - sending OBEX_HTTP_BAD_REQUEST"); 1276 return ResponseCodes.OBEX_HTTP_BAD_REQUEST; 1277 } 1278 } else { 1279 if(outStream != null) { try { outStream.close(); } catch (IOException e) {} } 1280 } 1281 return ResponseCodes.OBEX_HTTP_OK; 1282 } 1283 1284 /** 1285 * Generate and send the Folder listing response based on an application 1286 * parameter header. This function call will block until complete or aborted 1287 * by the peer. Fragmentation of packets larger than the obex packet size 1288 * will be handled by this function. 1289 * 1290 * @param op 1291 * The OBEX operation. 1292 * @param appParams 1293 * The application parameter header 1294 * @return {@link ResponseCodes.OBEX_HTTP_OK} on success or 1295 * {@link ResponseCodes.OBEX_HTTP_BAD_REQUEST} on error. 1296 */ 1297 private int sendFolderListingRsp(Operation op, BluetoothMapAppParams appParams){ 1298 OutputStream outStream = null; 1299 byte[] outBytes = null; 1300 BluetoothMapAppParams outAppParams = new BluetoothMapAppParams(); 1301 int maxChunkSize, bytesWritten = 0; 1302 HeaderSet replyHeaders = new HeaderSet(); 1303 int bytesToWrite, maxListCount, listStartOffset; 1304 if(appParams == null){ 1305 appParams = new BluetoothMapAppParams(); 1306 appParams.setMaxListCount(1024); 1307 } 1308 1309 if(V) 1310 Log.v(TAG,"sendFolderList for " + mCurrentFolder.getName()); 1311 1312 try { 1313 maxListCount = appParams.getMaxListCount(); 1314 listStartOffset = appParams.getStartOffset(); 1315 1316 if(listStartOffset == BluetoothMapAppParams.INVALID_VALUE_PARAMETER) 1317 listStartOffset = 0; 1318 1319 if(maxListCount == BluetoothMapAppParams.INVALID_VALUE_PARAMETER) 1320 maxListCount = 1024; 1321 1322 if(maxListCount != 0) 1323 { 1324 outBytes = mCurrentFolder.encode(listStartOffset, maxListCount); 1325 outStream = op.openOutputStream(); 1326 } else { 1327 // ESR08 specified that this shall only be included for MaxListCount=0 1328 outAppParams.setFolderListingSize(mCurrentFolder.getSubFolderCount()); 1329 op.noBodyHeader(); 1330 } 1331 1332 // Build and set the application parameter header 1333 replyHeaders.setHeader(HeaderSet.APPLICATION_PARAMETER, outAppParams.EncodeParams()); 1334 op.sendHeaders(replyHeaders); 1335 1336 } catch (IOException e1) { 1337 Log.w(TAG,"sendFolderListingRsp: IOException" + 1338 " - sending OBEX_HTTP_BAD_REQUEST Exception:", e1); 1339 if(outStream != null) { try { outStream.close(); } catch (IOException e) {} } 1340 if(mIsAborted == true) { 1341 if(D) Log.d(TAG, "sendFolderListingRsp Operation Aborted"); 1342 return ResponseCodes.OBEX_HTTP_OK; 1343 } else { 1344 return ResponseCodes.OBEX_HTTP_BAD_REQUEST; 1345 } 1346 } catch (IllegalArgumentException e1) { 1347 Log.w(TAG,"sendFolderListingRsp: IllegalArgumentException" + 1348 " - sending OBEX_HTTP_BAD_REQUEST Exception:", e1); 1349 if(outStream != null) { try { outStream.close(); } catch (IOException e) {} } 1350 return ResponseCodes.OBEX_HTTP_PRECON_FAILED; 1351 } 1352 1353 maxChunkSize = op.getMaxPacketSize(); // This must be called after setting the headers. 1354 1355 if(outBytes != null) { 1356 try { 1357 while (bytesWritten < outBytes.length && mIsAborted == false) { 1358 bytesToWrite = Math.min(maxChunkSize, outBytes.length - bytesWritten); 1359 outStream.write(outBytes, bytesWritten, bytesToWrite); 1360 bytesWritten += bytesToWrite; 1361 } 1362 } catch (IOException e) { 1363 // We were probably aborted or disconnected 1364 } finally { 1365 if(outStream != null) { try { outStream.close(); } catch (IOException e) {} } 1366 } 1367 if(V) 1368 Log.v(TAG,"sendFolderList sent " + bytesWritten+" bytes out of "+ outBytes.length); 1369 if(bytesWritten == outBytes.length || mIsAborted) 1370 return ResponseCodes.OBEX_HTTP_OK; 1371 else 1372 return ResponseCodes.OBEX_HTTP_BAD_REQUEST; 1373 } 1374 1375 return ResponseCodes.OBEX_HTTP_OK; 1376 } 1377 1378 /** 1379 * Generate and send the get MAS Instance Information response based on an MAS Instance 1380 * 1381 * @param op 1382 * The OBEX operation. 1383 * @param appParams 1384 * The application parameter header 1385 * @return {@link ResponseCodes.OBEX_HTTP_OK} on success or 1386 * {@link ResponseCodes.OBEX_HTTP_BAD_REQUEST} on error. 1387 */ 1388 private int sendMASInstanceInformationRsp(Operation op, BluetoothMapAppParams appParams){ 1389 1390 OutputStream outStream = null; 1391 byte[] outBytes = null; 1392 String outString = null; 1393 int maxChunkSize, bytesToWrite, bytesWritten = 0; 1394 1395 try { 1396 if(mMasId == appParams.getMasInstanceId()) { 1397 if(mAccount != null) { 1398 if(mAccount.getType() == TYPE.EMAIL) { 1399 outString = (mAccount.getName() != null) ? mAccount.getName() : 1400 BluetoothMapMasInstance.TYPE_EMAIL_STR; 1401 } else if(mAccount.getType() == TYPE.IM){ 1402 outString = mAccount.getUciFull(); 1403 if(outString == null) { 1404 String uci = mAccount.getUci(); 1405 // TODO: Do we need to align this with HF/PBAP 1406 StringBuilder sb = 1407 new StringBuilder(uci == null ? 5 : 5 + uci.length()); 1408 sb.append("un"); 1409 if(mMasId < 10) { 1410 sb.append("00"); 1411 } else if(mMasId < 100) { 1412 sb.append("0"); 1413 } 1414 sb.append(mMasId); 1415 if(uci != null) { 1416 sb.append(":").append(uci); 1417 } 1418 outString = sb.toString(); 1419 } 1420 } 1421 } else { 1422 outString = BluetoothMapMasInstance.TYPE_SMS_MMS_STR; 1423 // TODO: Add phone number if possible 1424 } 1425 } else { 1426 return ResponseCodes.OBEX_HTTP_BAD_REQUEST; 1427 } 1428 1429 /* Ensure byte array max length is 200 containing valid UTF-8 characters */ 1430 outBytes = BluetoothMapUtils.truncateUtf8StringToBytearray(outString, 1431 MAS_INSTANCE_INFORMATION_LENGTH); 1432 1433 // Open the OBEX body stream 1434 outStream = op.openOutputStream(); 1435 1436 } catch (IOException e) { 1437 Log.w(TAG,"sendMASInstanceInformationRsp: IOException" + 1438 " - sending OBEX_HTTP_BAD_REQUEST", e); 1439 if(mIsAborted == true) { 1440 if(D) Log.d(TAG, "sendMASInstanceInformationRsp Operation Aborted"); 1441 return ResponseCodes.OBEX_HTTP_OK; 1442 } else { 1443 return ResponseCodes.OBEX_HTTP_BAD_REQUEST; 1444 } 1445 } 1446 1447 maxChunkSize = op.getMaxPacketSize(); // This must be called after setting the headers. 1448 1449 if(outBytes != null) { 1450 try { 1451 while (bytesWritten < outBytes.length && mIsAborted == false) { 1452 bytesToWrite = Math.min(maxChunkSize, outBytes.length - bytesWritten); 1453 outStream.write(outBytes, bytesWritten, bytesToWrite); 1454 bytesWritten += bytesToWrite; 1455 } 1456 } catch (IOException e) { 1457 // We were probably aborted or disconnected 1458 } finally { 1459 if(outStream != null) { try { outStream.close(); } catch (IOException e) {} } 1460 } 1461 if(V) 1462 Log.v(TAG,"sendMASInstanceInformationRsp sent " + bytesWritten + 1463 " bytes out of "+ outBytes.length); 1464 if(bytesWritten == outBytes.length || mIsAborted) 1465 return ResponseCodes.OBEX_HTTP_OK; 1466 else 1467 return ResponseCodes.OBEX_HTTP_BAD_REQUEST; 1468 } 1469 return ResponseCodes.OBEX_HTTP_OK; 1470 } 1471 1472 /** 1473 * Generate and send the get message response based on an application 1474 * parameter header and a handle. 1475 * 1476 * @param op 1477 * The OBEX operation. 1478 * @param handle 1479 * The handle of the requested message 1480 * @param appParams 1481 * The application parameter header 1482 * @param version 1483 * The string representation of the version number(i.e. "1.0") 1484 * @return {@link ResponseCodes.OBEX_HTTP_OK} on success or 1485 * {@link ResponseCodes.OBEX_HTTP_BAD_REQUEST} on error. 1486 */ 1487 private int sendGetMessageRsp(Operation op, String handle, 1488 BluetoothMapAppParams appParams, String version){ 1489 OutputStream outStream = null; 1490 byte[] outBytes = null; 1491 int maxChunkSize, bytesToWrite, bytesWritten = 0; 1492 1493 try { 1494 outBytes = mOutContent.getMessage(handle, appParams, mCurrentFolder, version); 1495 outStream = op.openOutputStream(); 1496 1497 // If it is a fraction request of Email message, set header before responding 1498 if ((BluetoothMapUtils.getMsgTypeFromHandle(handle).equals(TYPE.EMAIL)|| 1499 (BluetoothMapUtils.getMsgTypeFromHandle(handle).equals(TYPE.IM))) && 1500 (appParams.getFractionRequest() == 1501 BluetoothMapAppParams.FRACTION_REQUEST_FIRST)) { 1502 BluetoothMapAppParams outAppParams = new BluetoothMapAppParams(); 1503 HeaderSet replyHeaders = new HeaderSet(); 1504 outAppParams.setFractionDeliver(BluetoothMapAppParams.FRACTION_DELIVER_LAST); 1505 // Build and set the application parameter header 1506 replyHeaders.setHeader(HeaderSet.APPLICATION_PARAMETER, 1507 outAppParams.EncodeParams()); 1508 op.sendHeaders(replyHeaders); 1509 if(V) Log.v(TAG,"sendGetMessageRsp fractionRequest - " + 1510 "set FRACTION_DELIVER_LAST header"); 1511 } 1512 1513 } catch (IOException e) { 1514 Log.w(TAG,"sendGetMessageRsp: IOException - sending OBEX_HTTP_BAD_REQUEST", e); 1515 if(outStream != null) { try { outStream.close(); } catch (IOException ex) {} } 1516 if(mIsAborted == true) { 1517 if(D) Log.d(TAG, "sendGetMessageRsp Operation Aborted"); 1518 return ResponseCodes.OBEX_HTTP_OK; 1519 } else { 1520 return ResponseCodes.OBEX_HTTP_BAD_REQUEST; 1521 } 1522 } catch (IllegalArgumentException e) { 1523 Log.w(TAG,"sendGetMessageRsp: IllegalArgumentException (e.g. invalid handle) - " + 1524 "sending OBEX_HTTP_BAD_REQUEST", e); 1525 if(outStream != null) { try { outStream.close(); } catch (IOException ex) {} } 1526 return ResponseCodes.OBEX_HTTP_BAD_REQUEST; 1527 } 1528 1529 maxChunkSize = op.getMaxPacketSize(); // This must be called after setting the headers. 1530 1531 if(outBytes != null) { 1532 try { 1533 while (bytesWritten < outBytes.length && mIsAborted == false) { 1534 bytesToWrite = Math.min(maxChunkSize, outBytes.length - bytesWritten); 1535 outStream.write(outBytes, bytesWritten, bytesToWrite); 1536 bytesWritten += bytesToWrite; 1537 } 1538 } catch (IOException e) { 1539 // We were probably aborted or disconnected 1540 if(D && e.getMessage().equals("Abort Received")) { 1541 Log.w(TAG, "getMessage() Aborted...", e); 1542 } 1543 } finally { 1544 if(outStream != null) { try { outStream.close(); } catch (IOException e) {} } 1545 } 1546 if(bytesWritten == outBytes.length || mIsAborted) 1547 return ResponseCodes.OBEX_HTTP_OK; 1548 else 1549 return ResponseCodes.OBEX_HTTP_BAD_REQUEST; 1550 } 1551 1552 return ResponseCodes.OBEX_HTTP_OK; 1553 } 1554 1555 @Override 1556 public int onDelete(HeaderSet request, HeaderSet reply) { 1557 if(D) Log.v(TAG, "onDelete() " + request.toString()); 1558 mIsAborted = false; 1559 notifyUpdateWakeLock(); 1560 String type, name; 1561 byte[] appParamRaw; 1562 BluetoothMapAppParams appParams = null; 1563 1564 /* TODO: If this is to be placed here, we need to cleanup - e.g. the exception handling */ 1565 try { 1566 type = (String)request.getHeader(HeaderSet.TYPE); 1567 1568 name = (String)request.getHeader(HeaderSet.NAME); 1569 appParamRaw = (byte[])request.getHeader(HeaderSet.APPLICATION_PARAMETER); 1570 if(appParamRaw != null) 1571 appParams = new BluetoothMapAppParams(appParamRaw); 1572 if(D) Log.d(TAG,"type = " + type + ", name = " + name); 1573 if(type.equals(TYPE_SET_NOTIFICATION_FILTER)) { 1574 if(V) { 1575 Log.d(TAG,"TYPE_SET_NOTIFICATION_FILTER: NotificationFilter: " 1576 + appParams.getNotificationFilter()); 1577 } 1578 mObserver.setNotificationFilter(appParams.getNotificationFilter()); 1579 return ResponseCodes.OBEX_HTTP_OK; 1580 } else if (type.equals(TYPE_SET_OWNER_STATUS)) { 1581 if(V) { 1582 Log.d(TAG,"TYPE_SET_OWNER_STATUS:" + 1583 " PresenceAvailability " + appParams.getPresenceAvailability() + 1584 ", PresenceStatus: " + appParams.getPresenceStatus() + 1585 ", LastActivity: " + appParams.getLastActivityString() + 1586 ", ChatStatus: " + appParams.getChatState() + 1587 ", ChatStatusConvoId: " + appParams.getChatStateConvoIdString()); 1588 } 1589 return setOwnerStatus(name, appParams); 1590 } 1591 1592 } catch (RemoteException e){ 1593 //reload the providerClient and return error 1594 try { 1595 mProviderClient = acquireUnstableContentProviderOrThrow(); 1596 }catch (RemoteException e2){ 1597 //should not happen 1598 } 1599 return ResponseCodes.OBEX_HTTP_BAD_REQUEST; 1600 }catch (Exception e) { 1601 1602 if(D) { 1603 Log.e(TAG, "Exception occured while handling request",e); 1604 } else { 1605 Log.e(TAG, "Exception occured while handling request"); 1606 } 1607 if(mIsAborted) { 1608 return ResponseCodes.OBEX_HTTP_OK; 1609 } else { 1610 return ResponseCodes.OBEX_HTTP_BAD_REQUEST; 1611 } 1612 } 1613 return ResponseCodes.OBEX_HTTP_BAD_REQUEST; 1614 } 1615 1616 private void notifyUpdateWakeLock() { 1617 if(mCallback != null) { 1618 Message msg = Message.obtain(mCallback); 1619 msg.what = BluetoothMapService.MSG_ACQUIRE_WAKE_LOCK; 1620 msg.sendToTarget(); 1621 } 1622 } 1623 1624 private static final void logHeader(HeaderSet hs) { 1625 Log.v(TAG, "Dumping HeaderSet " + hs.toString()); 1626 try { 1627 Log.v(TAG, "CONNECTION_ID : " + hs.getHeader(HeaderSet.CONNECTION_ID)); 1628 Log.v(TAG, "NAME : " + hs.getHeader(HeaderSet.NAME)); 1629 Log.v(TAG, "TYPE : " + hs.getHeader(HeaderSet.TYPE)); 1630 Log.v(TAG, "TARGET : " + hs.getHeader(HeaderSet.TARGET)); 1631 Log.v(TAG, "WHO : " + hs.getHeader(HeaderSet.WHO)); 1632 Log.v(TAG, "APPLICATION_PARAMETER : " + hs.getHeader(HeaderSet.APPLICATION_PARAMETER)); 1633 } catch (IOException e) { 1634 Log.e(TAG, "dump HeaderSet error " + e); 1635 } 1636 Log.v(TAG, "NEW!!! Dumping HeaderSet END"); 1637 } 1638} 1639