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.text.format.DateUtils; 29 import android.util.Log; 30 31 import com.android.bluetooth.SignedLongLong; 32 import com.android.bluetooth.map.BluetoothMapUtils.TYPE; 33 import com.android.bluetooth.map.BluetoothMapMasInstance; 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, (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 BluetoothMapObexServer(Handler callback, Context context, BluetoothMapContentObserver observer, BluetoothMapMasInstance mas, BluetoothMapAccountItem account, boolean enableSmsMms)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 */ acquireUnstableContentProviderOrThrow()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 */ buildFolderStructure()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 */ addBaseFolders(BluetoothMapFolderElement root)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 */ addSmsMmsFolders(BluetoothMapFolderElement root)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 addImFolders(BluetoothMapFolderElement root)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 */ addEmailFolders(BluetoothMapFolderElement parentFolder)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 isSrmSupported()290 public boolean isSrmSupported() { 291 // TODO: Update based on the transport used 292 return true; 293 } 294 getRemoteFeatureMask()295 public int getRemoteFeatureMask() { 296 return mRemoteFeatureMask; 297 } 298 setRemoteFeatureMask(int mRemoteFeatureMask)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 onConnect(final HeaderSet request, HeaderSet reply)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 onDisconnect(final HeaderSet req, final HeaderSet resp)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 onAbort(HeaderSet request, HeaderSet reply)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 isUserUnlocked()401 private boolean isUserUnlocked() { 402 UserManager manager = UserManager.get(mContext); 403 return (manager == null || manager.isUserUnlocked()); 404 } 405 406 @Override onPut(final Operation op)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 updateInbox()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 getFolderElementFromName(String folderName)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 pushMessage(final Operation op, String folderName, BluetoothMapAppParams appParams, String messageVersion)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 setMessageStatus(String msgHandle, BluetoothMapAppParams appParams)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 setOwnerStatus(String msgHandle, BluetoothMapAppParams appParams)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 onSetPath(final HeaderSet request, final HeaderSet reply, final boolean backup, final boolean create)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 onClose()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 onGet(Operation op)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 */ sendMessageListingRsp(Operation op, BluetoothMapAppParams appParams, String folderName)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 */ setMsgTypeFilterParams(BluetoothMapAppParams appParams, boolean overwrite)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 */ sendConvoListingRsp(Operation op, BluetoothMapAppParams appParams, String folderName)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 */ sendFolderListingRsp(Operation op, BluetoothMapAppParams appParams)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 */ sendMASInstanceInformationRsp(Operation op, BluetoothMapAppParams appParams)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 */ sendGetMessageRsp(Operation op, String handle, BluetoothMapAppParams appParams, String version)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 onDelete(HeaderSet request, HeaderSet reply)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 notifyUpdateWakeLock()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 logHeader(HeaderSet hs)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