BluetoothMapObexServer.java revision 70be005a18a35ec5fcb46152f0dfbe82156efa3a
1/* 2* Copyright (C) 2013 Samsung System LSI 3* Licensed under the Apache License, Version 2.0 (the "License"); 4* you may not use this file except in compliance with the License. 5* You may obtain a copy of the License at 6* 7* http://www.apache.org/licenses/LICENSE-2.0 8* 9* Unless required by applicable law or agreed to in writing, software 10* distributed under the License is distributed on an "AS IS" BASIS, 11* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12* See the License for the specific language governing permissions and 13* limitations under the License. 14*/ 15package com.android.bluetooth.map; 16 17import java.io.IOException; 18import java.io.InputStream; 19import java.io.OutputStream; 20import java.util.Arrays; 21import java.util.Calendar; 22 23import javax.obex.HeaderSet; 24import javax.obex.Operation; 25import javax.obex.ResponseCodes; 26import javax.obex.ServerRequestHandler; 27 28import com.android.bluetooth.map.BluetoothMapUtils; 29import com.android.bluetooth.map.BluetoothMapUtils.TYPE; 30 31import android.content.Context; 32import android.os.Handler; 33import android.os.Message; 34import android.util.Log; 35 36public class BluetoothMapObexServer extends ServerRequestHandler { 37 38 private static final String TAG = "BluetoothMapObexServer"; 39 40 private static final boolean D = BluetoothMapService.DEBUG; 41 private static final boolean V = BluetoothMapService.VERBOSE; 42 43 private static final int UUID_LENGTH = 16; 44 45 // 128 bit UUID for MAP 46 private static final byte[] MAP_TARGET = new byte[] { 47 (byte)0xBB, (byte)0x58, (byte)0x2B, (byte)0x40, 48 (byte)0x42, (byte)0x0C, (byte)0x11, (byte)0xDB, 49 (byte)0xB0, (byte)0xDE, (byte)0x08, (byte)0x00, 50 (byte)0x20, (byte)0x0C, (byte)0x9A, (byte)0x66 51 }; 52 53 /* Message types */ 54 private static final String TYPE_GET_FOLDER_LISTING = "x-obex/folder-listing"; 55 private static final String TYPE_GET_MESSAGE_LISTING = "x-bt/MAP-msg-listing"; 56 private static final String TYPE_MESSAGE = "x-bt/message"; 57 private static final String TYPE_SET_MESSAGE_STATUS = "x-bt/messageStatus"; 58 private static final String TYPE_SET_NOTIFICATION_REGISTRATION = "x-bt/MAP-NotificationRegistration"; 59 private static final String TYPE_MESSAGE_UPDATE = "x-bt/MAP-messageUpdate"; 60 61 private BluetoothMapFolderElement mCurrentFolder; 62 63 private Handler mCallback = null; 64 65 private Context mContext; 66 67 public static boolean sIsAborted = false; 68 69 BluetoothMapContent mOutContent; 70 71 public BluetoothMapObexServer(Handler callback, Context context) { 72 super(); 73 mCallback = callback; 74 mContext = context; 75 mOutContent = new BluetoothMapContent(mContext); 76 77 buildFolderStructure(); /* Build the default folder structure, and set 78 mCurrentFolder to root folder */ 79 } 80 81 /** 82 * Build the default minimal folder structure, as defined in the MAP specification. 83 */ 84 private void buildFolderStructure(){ 85 mCurrentFolder = new BluetoothMapFolderElement("root", null); // This will be the root element 86 BluetoothMapFolderElement tmpFolder; 87 tmpFolder = mCurrentFolder.addFolder("telecom"); // root/telecom 88 tmpFolder = tmpFolder.addFolder("msg"); // root/telecom/msg 89 tmpFolder.addFolder("inbox"); // root/telecom/msg/inbox 90 tmpFolder.addFolder("outbox"); 91 tmpFolder.addFolder("sent"); 92 tmpFolder.addFolder("deleted"); 93 tmpFolder.addFolder("draft"); 94 } 95 96 @Override 97 public int onConnect(final HeaderSet request, HeaderSet reply) { 98 if (D) Log.d(TAG, "onConnect():"); 99 if (V) logHeader(request); 100 try { 101 byte[] uuid = (byte[])request.getHeader(HeaderSet.TARGET); 102 if (uuid == null) { 103 return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE; 104 } 105 if (D) Log.d(TAG, "onConnect(): uuid=" + Arrays.toString(uuid)); 106 107 if (uuid.length != UUID_LENGTH) { 108 Log.w(TAG, "Wrong UUID length"); 109 return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE; 110 } 111 for (int i = 0; i < UUID_LENGTH; i++) { 112 if (uuid[i] != MAP_TARGET[i]) { 113 Log.w(TAG, "Wrong UUID"); 114 return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE; 115 } 116 } 117 reply.setHeader(HeaderSet.WHO, uuid); 118 } catch (IOException e) { 119 Log.e(TAG, e.toString()); 120 return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR; 121 } 122 123 try { 124 byte[] remote = (byte[])request.getHeader(HeaderSet.WHO); 125 if (remote != null) { 126 if (D) Log.d(TAG, "onConnect(): remote=" + Arrays.toString(remote)); 127 reply.setHeader(HeaderSet.TARGET, remote); 128 } 129 } catch (IOException e) { 130 Log.e(TAG, e.toString()); 131 return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR; 132 } 133 134 if (V) Log.v(TAG, "onConnect(): uuid is ok, will send out " + 135 "MSG_SESSION_ESTABLISHED msg."); 136 137 138 Message msg = Message.obtain(mCallback); 139 msg.what = BluetoothMapService.MSG_SESSION_ESTABLISHED; 140 msg.sendToTarget(); 141 142 return ResponseCodes.OBEX_HTTP_OK; 143 } 144 145 @Override 146 public void onDisconnect(final HeaderSet req, final HeaderSet resp) { 147 if (D) Log.d(TAG, "onDisconnect(): enter"); 148 if (V) logHeader(req); 149 150 resp.responseCode = ResponseCodes.OBEX_HTTP_OK; 151 if (mCallback != null) { 152 Message msg = Message.obtain(mCallback); 153 msg.what = BluetoothMapService.MSG_SESSION_DISCONNECTED; 154 msg.sendToTarget(); 155 if (V) Log.v(TAG, "onDisconnect(): msg MSG_SESSION_DISCONNECTED sent out."); 156 } 157 } 158 159 @Override 160 public int onAbort(HeaderSet request, HeaderSet reply) { 161 if (D) Log.d(TAG, "onAbort(): enter."); 162 sIsAborted = true; 163 return ResponseCodes.OBEX_HTTP_OK; 164 } 165 166 @Override 167 public int onPut(final Operation op) { 168 if (D) Log.d(TAG, "onPut(): enter"); 169 HeaderSet request = null; 170 String type, name; 171 byte[] appParamRaw; 172 BluetoothMapAppParams appParams = null; 173 174 try { 175 request = op.getReceivedHeader(); 176 type = (String)request.getHeader(HeaderSet.TYPE); 177 name = (String)request.getHeader(HeaderSet.NAME); 178 appParamRaw = (byte[])request.getHeader(HeaderSet.APPLICATION_PARAMETER); 179 if(appParamRaw != null) 180 appParams = new BluetoothMapAppParams(appParamRaw); 181 } catch (Exception e) { 182 Log.e(TAG, "request headers error"); 183 return ResponseCodes.OBEX_HTTP_BAD_REQUEST; 184 } 185 186 if(D) Log.d(TAG,"type = " + type + ", name = " + name); 187 if (type.equals(TYPE_MESSAGE_UPDATE)) { 188 if(V) { 189 Log.d(TAG,"TYPE_MESSAGE_UPDATE:"); 190 } 191 return ResponseCodes.OBEX_HTTP_OK; 192 }else if(type.equals(TYPE_SET_NOTIFICATION_REGISTRATION)) { 193 if(V) { 194 Log.d(TAG,"TYPE_SET_NOTIFICATION_REGISTRATION: NotificationStatus: " + appParams.getNotificationStatus()); 195 } 196 return setNotificationRegistration(appParams); 197 }else if(type.equals(TYPE_SET_MESSAGE_STATUS)) { 198 if(V) { 199 Log.d(TAG,"TYPE_SET_MESSAGE_STATUS: StatusIndicator: " + appParams.getStatusIndicator() + ", StatusValue: " + appParams.getStatusValue()); 200 } 201 return setMessageStatus(name, appParams); 202 } else if (type.equals(TYPE_MESSAGE)) { 203 if(V) { 204 Log.d(TAG,"TYPE_MESSAGE: Transparet: " + appParams.getTransparent() + ", Retry: " + appParams.getRetry()); 205 Log.d(TAG," charset: " + appParams.getCharset()); 206 } 207 return pushMessage(op, name, appParams); 208 209 } 210 211 return ResponseCodes.OBEX_HTTP_BAD_REQUEST; 212 } 213 214 private int setNotificationRegistration(BluetoothMapAppParams appParams) { 215 // Forward the request to the MNS thread as a message - including the MAS instance ID. 216 Handler mns = BluetoothMnsObexClient.getMessageHandler(); 217 if(mns != null) { 218 Message msg = Message.obtain(mns); 219 msg.what = BluetoothMnsObexClient.MSG_MNS_NOTIFICATION_REGISTRATION; 220 msg.arg1 = 0; // TODO: Add correct MAS ID, as specified in the SDP record. 221 msg.arg2 = appParams.getNotificationStatus(); 222 msg.sendToTarget(); 223 return ResponseCodes.OBEX_HTTP_OK; 224 } else { 225 return ResponseCodes.OBEX_HTTP_UNAVAILABLE; // This should not happen. 226 } 227 } 228 229 private int pushMessage(final Operation op, String folderName, BluetoothMapAppParams appParams) { 230 if(appParams.getCharset() == BluetoothMapAppParams.INVALID_VALUE_PARAMETER) { 231 if(D) Log.d(TAG, "Missing charset - unable to decode message content. appParams.getCharset() = " + appParams.getCharset()); 232 return ResponseCodes.OBEX_HTTP_PRECON_FAILED; 233 } 234 try { 235 if(folderName == null || folderName.equals("")) { 236 folderName = mCurrentFolder.getName(); 237 } 238 if(!folderName.equals("outbox") && !folderName.equals("draft")) { 239 if(D) Log.d(TAG, "Push message only allowed to outbox and draft. folderName: " + folderName); 240 return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE; 241 } 242 /* - Read out the message 243 * - Decode into a bMessage 244 * - send it. 245 */ 246 InputStream bMsgStream; 247 BluetoothMapbMessage message; 248 bMsgStream = op.openInputStream(); 249 message = BluetoothMapbMessage.parse(bMsgStream, appParams.getCharset()); // Decode the messageBody 250 // Send message 251 BluetoothMapContentObserver observer = BluetoothMnsObexClient.getContentObserver(); 252 if (observer == null) { 253 return ResponseCodes.OBEX_HTTP_UNAVAILABLE; // Should not happen. 254 } 255 256 long handle = observer.pushMessage(message, folderName, appParams); 257 if (D) Log.d(TAG, "pushMessage handle: " + handle); 258 if (handle < 0) { 259 return ResponseCodes.OBEX_HTTP_UNAVAILABLE; // Should not happen. 260 } 261 HeaderSet replyHeaders = new HeaderSet(); 262 String handleStr = BluetoothMapUtils.getMapHandle(handle, message.getType()); 263 if (D) Log.d(TAG, "handleStr: " + handleStr + " message.getType(): " + message.getType()); 264 replyHeaders.setHeader(HeaderSet.NAME, handleStr); 265 op.sendHeaders(replyHeaders); 266 267 bMsgStream.close(); 268 } catch (IllegalArgumentException e) { 269 if(D) Log.w(TAG, "Wrongly formatted bMessage received", e); 270 return ResponseCodes.OBEX_HTTP_PRECON_FAILED; 271 } catch (Exception e) { 272 // TODO: Change to IOException after debug 273 Log.e(TAG, "Exception occured: ", e); 274 return ResponseCodes.OBEX_HTTP_BAD_REQUEST; 275 } 276 return ResponseCodes.OBEX_HTTP_OK; 277 } 278 279 private int setMessageStatus(String msgHandle, BluetoothMapAppParams appParams) { 280 int indicator = appParams.getStatusIndicator(); 281 int value = appParams.getStatusValue(); 282 long handle; 283 BluetoothMapUtils.TYPE msgType; 284 285 if(indicator == BluetoothMapAppParams.INVALID_VALUE_PARAMETER || 286 value == BluetoothMapAppParams.INVALID_VALUE_PARAMETER || 287 msgHandle == null) { 288 return ResponseCodes.OBEX_HTTP_PRECON_FAILED; 289 } 290 BluetoothMapContentObserver observer = BluetoothMnsObexClient.getContentObserver(); 291 if (observer == null) { 292 return ResponseCodes.OBEX_HTTP_UNAVAILABLE; // Should not happen. 293 } 294 295 try { 296 handle = BluetoothMapUtils.getCpHandle(msgHandle); 297 msgType = BluetoothMapUtils.getMsgTypeFromHandle(msgHandle); 298 } catch (NumberFormatException e) { 299 Log.w(TAG, "Wrongly formatted message handle: " + msgHandle); 300 return ResponseCodes.OBEX_HTTP_PRECON_FAILED; 301 } 302 303 if( indicator == BluetoothMapAppParams.STATUS_INDICATOR_DELETED) { 304 if (!observer.setMessageStatusDeleted(handle, msgType, value)) { 305 return ResponseCodes.OBEX_HTTP_UNAVAILABLE; 306 } 307 } else /* BluetoothMapAppParams.STATUS_INDICATOR_READE */ { 308 if (!observer.setMessageStatusRead(handle, msgType, value)) { 309 return ResponseCodes.OBEX_HTTP_UNAVAILABLE; 310 } 311 } 312 return ResponseCodes.OBEX_HTTP_OK; 313 } 314 315 @Override 316 public int onSetPath(final HeaderSet request, final HeaderSet reply, final boolean backup, 317 final boolean create) { 318 String folderName; 319 BluetoothMapFolderElement folder; 320 try { 321 folderName = (String)request.getHeader(HeaderSet.NAME); 322 } catch (Exception e) { 323 Log.e(TAG, "request headers error"); 324 return ResponseCodes.OBEX_HTTP_BAD_REQUEST; 325 } 326 327 if (V) logHeader(request); 328 if (D) Log.d(TAG, "onSetPath name is " + folderName + " backup: " + backup 329 + "create: " + create); 330 331 if(backup == true){ 332 if(mCurrentFolder.getParent() != null) 333 mCurrentFolder = mCurrentFolder.getParent(); 334 else 335 return ResponseCodes.OBEX_HTTP_BAD_REQUEST; 336 } 337 338 if (folderName == null || folderName == "") { 339 if(backup == false) 340 mCurrentFolder = mCurrentFolder.getRoot(); 341 } 342 else { 343 folder = mCurrentFolder.getSubFolder(folderName); 344 if(folder != null) 345 mCurrentFolder = folder; 346 else 347 return ResponseCodes.OBEX_HTTP_BAD_REQUEST; 348 } 349 if (V) Log.d(TAG, "Current Folder: " + mCurrentFolder.getName()); 350 return ResponseCodes.OBEX_HTTP_OK; 351 } 352 353 @Override 354 public void onClose() { 355 if (mCallback != null) { 356 Message msg = Message.obtain(mCallback); 357 msg.what = BluetoothMapService.MSG_SERVERSESSION_CLOSE; 358 msg.sendToTarget(); 359 if (D) Log.d(TAG, "onClose(): msg MSG_SERVERSESSION_CLOSE sent out."); 360 } 361 } 362 363 @Override 364 public int onGet(Operation op) { 365 sIsAborted = false; 366 HeaderSet request; 367 String type; 368 String name; 369 byte[] appParamRaw = null; 370 BluetoothMapAppParams appParams = null; 371 try { 372 request = op.getReceivedHeader(); 373 type = (String)request.getHeader(HeaderSet.TYPE); 374 name = (String)request.getHeader(HeaderSet.NAME); 375 appParamRaw = (byte[])request.getHeader(HeaderSet.APPLICATION_PARAMETER); 376 if(appParamRaw != null) 377 appParams = new BluetoothMapAppParams(appParamRaw); 378 379 if (V) logHeader(request); 380 if (D) Log.d(TAG, "OnGet type is " + type + " name is " + name); 381 382 if (type == null) { 383 if (V) Log.d(TAG, "type is null?" + type); 384 return ResponseCodes.OBEX_HTTP_BAD_REQUEST; 385 } 386 387 if (type.equals(TYPE_GET_FOLDER_LISTING)) { 388 if (V && appParams != null) { 389 Log.d(TAG,"TYPE_GET_FOLDER_LISTING: MaxListCount = " + appParams.getMaxListCount() + 390 ", ListStartOffset = " + appParams.getStartOffset()); 391 } 392 return sendFolderListingRsp(op, appParams); // Block until all packets have been send. 393 } 394 else if (type.equals(TYPE_GET_MESSAGE_LISTING)){ 395 if (V && appParams != null) { 396 Log.d(TAG,"TYPE_GET_MESSAGE_LISTING: MaxListCount = " + appParams.getMaxListCount() + 397 ", ListStartOffset = " + appParams.getStartOffset()); 398 Log.d(TAG,"SubjectLength = " + appParams.getSubjectLength() + ", ParameterMask = " + 399 appParams.getParameterMask()); 400 Log.d(TAG,"FilterMessageType = " + appParams.getFilterMessageType() + 401 ", FilterPeriodBegin = " + appParams.getFilterPeriodBegin()); 402 Log.d(TAG,"FilterPeriodEnd = " + appParams.getFilterPeriodBegin() + 403 ", FilterReadStatus = " + appParams.getFilterReadStatus()); 404 Log.d(TAG,"FilterRecipient = " + appParams.getFilterRecipient() + 405 ", FilterOriginator = " + appParams.getFilterOriginator()); 406 Log.d(TAG,"FilterPriority = " + appParams.getFilterPriority()); 407 } 408 return sendMessageListingRsp(op, appParams, name); // Block until all packets have been send. 409 } 410 else if (type.equals(TYPE_MESSAGE)){ 411 if(V && appParams != null) { 412 Log.d(TAG,"TYPE_MESSAGE (GET): Attachment = " + appParams.getAttachment() + ", Charset = " + appParams.getCharset() + 413 ", FractionRequest = " + appParams.getFractionRequest()); 414 } 415 return sendGetMessageRsp(op, name, appParams); // Block until all packets have been send. 416 } 417 else { 418 Log.w(TAG, "unknown type request: " + type); 419 return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE; 420 } 421 } catch (Exception e) { 422 // TODO: Move to the part that actually throws exceptions, and change to the correat exception type 423 Log.e(TAG, "request headers error, Exception:", e); 424 return ResponseCodes.OBEX_HTTP_BAD_REQUEST; 425 } 426 } 427 428 /** 429 * Generate and send the message listing response based on an application 430 * parameter header. This function call will block until complete or aborted 431 * by the peer. Fragmentation of packets larger than the obex packet size 432 * will be handled by this function. 433 * 434 * @param op 435 * The OBEX operation. 436 * @param appParams 437 * The application parameter header 438 * @return {@link ResponseCodes.OBEX_HTTP_OK} on success or 439 * {@link ResponseCodes.OBEX_HTTP_BAD_REQUEST} on error. 440 */ 441 private int sendMessageListingRsp(Operation op, BluetoothMapAppParams appParams, String folderName){ 442 OutputStream outStream = null; 443 byte[] outBytes = null; 444 int maxChunkSize, bytesToWrite, bytesWritten = 0, listSize; 445 boolean hasUnread = false; 446 HeaderSet replyHeaders = new HeaderSet(); 447 BluetoothMapAppParams outAppParams = new BluetoothMapAppParams(); 448 BluetoothMapMessageListing outList; 449 if(folderName == null) { 450 folderName = mCurrentFolder.getName(); 451 } 452 if(appParams == null){ 453 appParams = new BluetoothMapAppParams(); 454 appParams.setMaxListCount(1024); 455 appParams.setStartOffset(0); 456 } 457 458 // Check to see if we only need to send the size - hence no need to encode. 459 try { 460 // Open the OBEX body stream 461 outStream = op.openOutputStream(); 462 463 if(appParams.getMaxListCount() == BluetoothMapAppParams.INVALID_VALUE_PARAMETER) 464 appParams.setMaxListCount(1024); 465 466 if(appParams.getStartOffset() == BluetoothMapAppParams.INVALID_VALUE_PARAMETER) 467 appParams.setStartOffset(0); 468 469 if(appParams.getMaxListCount() != 0) { 470 outList = mOutContent.msgListing(folderName, appParams); 471 // Generate the byte stream 472 outAppParams.setMessageListingSize(outList.getCount()); 473 outBytes = outList.encode(); 474 hasUnread = outList.hasUnread(); 475 } 476 else { 477 listSize = mOutContent.msgListingSize(folderName, appParams); 478 hasUnread = mOutContent.msgListingHasUnread(folderName, appParams); 479 outAppParams.setMessageListingSize(listSize); 480 op.noBodyHeader(); 481 } 482 483 // Build the application parameter header 484 485 // let the peer know if there are unread messages in the list 486 if(hasUnread) 487 { 488 outAppParams.setNewMessage(1); 489 }else{ 490 outAppParams.setNewMessage(0); 491 } 492 493 outAppParams.setMseTime(Calendar.getInstance().getTime().getTime()); 494 replyHeaders.setHeader(HeaderSet.APPLICATION_PARAMETER, outAppParams.EncodeParams()); 495 op.sendHeaders(replyHeaders); 496 497 } catch (IOException e) { 498 Log.w(TAG,"sendMessageListingRsp: IOException - sending OBEX_HTTP_BAD_REQUEST", e); 499 return ResponseCodes.OBEX_HTTP_BAD_REQUEST; 500 } catch (IllegalArgumentException e) { 501 Log.w(TAG,"sendMessageListingRsp: IllegalArgumentException - sending OBEX_HTTP_BAD_REQUEST", e); 502 return ResponseCodes.OBEX_HTTP_BAD_REQUEST; 503 } 504 505 maxChunkSize = op.getMaxPacketSize(); // This must be called after setting the headers. 506 if(outBytes != null) { 507 try { 508 while (bytesWritten < outBytes.length && sIsAborted == false) { 509 bytesToWrite = Math.min(maxChunkSize, outBytes.length - bytesWritten); 510 outStream.write(outBytes, bytesWritten, bytesToWrite); 511 bytesWritten += bytesToWrite; 512 } 513 } catch (IOException e) { 514 if(V) Log.w(TAG,e); 515 // We were probably aborted or disconnected 516 } finally { 517 if(outStream != null) { 518 try { 519 outStream.close(); 520 } catch (IOException e) { 521 // If an error occurs during close, there is no more cleanup to do 522 } 523 } 524 } 525 if(bytesWritten != outBytes.length) 526 return ResponseCodes.OBEX_HTTP_BAD_REQUEST; 527 } else { 528 try { 529 outStream.close(); 530 } catch (IOException e) { 531 // If an error occurs during close, there is no more cleanup to do 532 } 533 } 534 return ResponseCodes.OBEX_HTTP_OK; 535 } 536 537 /** 538 * Generate and send the Folder listing response based on an application 539 * parameter header. This function call will block until complete or aborted 540 * by the peer. Fragmentation of packets larger than the obex packet size 541 * will be handled by this function. 542 * 543 * @param op 544 * The OBEX operation. 545 * @param appParams 546 * The application parameter header 547 * @return {@link ResponseCodes.OBEX_HTTP_OK} on success or 548 * {@link ResponseCodes.OBEX_HTTP_BAD_REQUEST} on error. 549 */ 550 private int sendFolderListingRsp(Operation op, BluetoothMapAppParams appParams){ 551 OutputStream outStream = null; 552 byte[] outBytes = null; 553 BluetoothMapAppParams outAppParams = new BluetoothMapAppParams(); 554 int maxChunkSize, bytesWritten = 0; 555 HeaderSet replyHeaders = new HeaderSet(); 556 int bytesToWrite, maxListCount, listStartOffset; 557 if(appParams == null){ 558 appParams = new BluetoothMapAppParams(); 559 appParams.setMaxListCount(1024); 560 } 561 562 if(V) 563 Log.v(TAG,"sendFolderList for " + mCurrentFolder.getName()); 564 565 try { 566 maxListCount = appParams.getMaxListCount(); 567 listStartOffset = appParams.getStartOffset(); 568 569 if(listStartOffset == BluetoothMapAppParams.INVALID_VALUE_PARAMETER) 570 listStartOffset = 0; 571 572 if(maxListCount == BluetoothMapAppParams.INVALID_VALUE_PARAMETER) 573 maxListCount = 1024; 574 575 if(maxListCount != 0) 576 { 577 outBytes = mCurrentFolder.encode(listStartOffset, maxListCount); 578 outStream = op.openOutputStream(); 579 } 580 581 // Build and set the application parameter header 582 outAppParams.setFolderListingSize(mCurrentFolder.getSubFolderCount()); 583 replyHeaders.setHeader(HeaderSet.APPLICATION_PARAMETER, outAppParams.EncodeParams()); 584 op.sendHeaders(replyHeaders); 585 586 } catch (IOException e1) { 587 Log.w(TAG,"sendFolderListingRsp: IOException - sending OBEX_HTTP_BAD_REQUEST Exception:", e1); 588 return ResponseCodes.OBEX_HTTP_BAD_REQUEST; 589 } catch (IllegalArgumentException e1) { 590 Log.w(TAG,"sendFolderListingRsp: IllegalArgumentException - sending OBEX_HTTP_BAD_REQUEST Exception:", e1); 591 return ResponseCodes.OBEX_HTTP_BAD_REQUEST; 592 } 593 594 maxChunkSize = op.getMaxPacketSize(); // This must be called after setting the headers. 595 596 if(outBytes != null) { 597 try { 598 while (bytesWritten < outBytes.length && sIsAborted == false) { 599 bytesToWrite = Math.min(maxChunkSize, outBytes.length - bytesWritten); 600 outStream.write(outBytes, bytesWritten, bytesToWrite); 601 bytesWritten += bytesToWrite; 602 } 603 } catch (IOException e) { 604 // We were probably aborted or disconnected 605 } finally { 606 if(outStream != null) { 607 try { 608 outStream.close(); 609 } catch (IOException e) { 610 // If an error occurs during close, there is no more cleanup to do 611 } 612 } 613 } 614 if(V) 615 Log.v(TAG,"sendFolderList sent " + bytesWritten + " bytes out of "+ outBytes.length); 616 if(bytesWritten == outBytes.length) 617 return ResponseCodes.OBEX_HTTP_OK; 618 else 619 return ResponseCodes.OBEX_HTTP_BAD_REQUEST; 620 } 621 622 return ResponseCodes.OBEX_HTTP_OK; 623 } 624 625 /** 626 * Generate and send the get message response based on an application 627 * parameter header and a handle. 628 * 629 * @param op 630 * The OBEX operation. 631 * @param appParams 632 * The application parameter header 633 * @param handle 634 * The handle of the requested message 635 * @return {@link ResponseCodes.OBEX_HTTP_OK} on success or 636 * {@link ResponseCodes.OBEX_HTTP_BAD_REQUEST} on error. 637 */ 638 private int sendGetMessageRsp(Operation op, String handle, BluetoothMapAppParams appParams){ 639 OutputStream outStream ; 640 byte[] outBytes; 641 int maxChunkSize, bytesToWrite, bytesWritten = 0; 642 long msgHandle; 643 644 try { 645 outBytes = mOutContent.getMessage(handle, appParams); 646 outStream = op.openOutputStream(); 647 648 } catch (IOException e) { 649 Log.w(TAG,"sendGetMessageRsp: IOException - sending OBEX_HTTP_BAD_REQUEST", e); 650 return ResponseCodes.OBEX_HTTP_BAD_REQUEST; 651 } catch (IllegalArgumentException e) { 652 Log.w(TAG,"sendGetMessageRsp: IllegalArgumentException (e.g. invalid handle) - sending OBEX_HTTP_BAD_REQUEST", e); 653 return ResponseCodes.OBEX_HTTP_BAD_REQUEST; 654 } 655 656 maxChunkSize = op.getMaxPacketSize(); // This must be called after setting the headers. 657 658 if(outBytes != null) { 659 try { 660 while (bytesWritten < outBytes.length && sIsAborted == false) { 661 bytesToWrite = Math.min(maxChunkSize, outBytes.length - bytesWritten); 662 outStream.write(outBytes, bytesWritten, bytesToWrite); 663 bytesWritten += bytesToWrite; 664 } 665 } catch (IOException e) { 666 // We were probably aborted or disconnected 667 } finally { 668 if(outStream != null) { 669 try { 670 outStream.close(); 671 } catch (IOException e) { 672 // If an error occurs during close, there is no more cleanup to do 673 } 674 } 675 } 676 if(bytesWritten == outBytes.length) 677 return ResponseCodes.OBEX_HTTP_OK; 678 else 679 return ResponseCodes.OBEX_HTTP_BAD_REQUEST; 680 } 681 682 return ResponseCodes.OBEX_HTTP_OK; 683 } 684 685 686 private static final void logHeader(HeaderSet hs) { 687 Log.v(TAG, "Dumping HeaderSet " + hs.toString()); 688 try { 689 Log.v(TAG, "CONNECTION_ID : " + hs.getHeader(HeaderSet.CONNECTION_ID)); 690 Log.v(TAG, "NAME : " + hs.getHeader(HeaderSet.NAME)); 691 Log.v(TAG, "TYPE : " + hs.getHeader(HeaderSet.TYPE)); 692 Log.v(TAG, "TARGET : " + hs.getHeader(HeaderSet.TARGET)); 693 Log.v(TAG, "WHO : " + hs.getHeader(HeaderSet.WHO)); 694 Log.v(TAG, "APPLICATION_PARAMETER : " + hs.getHeader(HeaderSet.APPLICATION_PARAMETER)); 695 } catch (IOException e) { 696 Log.e(TAG, "dump HeaderSet error " + e); 697 } 698 Log.v(TAG, "NEW!!! Dumping HeaderSet END"); 699 } 700} 701