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