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