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