BluetoothPbapObexServer.java revision 2c282d5898ac0916470ebfa9ff26ba784cf4bb24
1 2package com.android.bluetooth.pbap; 3 4import android.os.Message; 5import android.os.Handler; 6import android.util.Log; 7 8import java.io.IOException; 9import java.io.OutputStream; 10import java.io.PrintWriter; 11import java.util.ArrayList; 12 13import javax.obex.ServerRequestHandler; 14import javax.obex.ResponseCodes; 15import javax.obex.ApplicationParameter; 16import javax.obex.Operation; 17import javax.obex.HeaderSet; 18 19public class BluetoothPbapObexServer extends ServerRequestHandler { 20 21 private static final String TAG = "BluetoothPbapObexServer"; 22 23 private static final int UUID_LENGTH = 16; 24 25 private static final int LEAGAL_PATH_NUM = 10; 26 27 private static final int VCARD_NAME_MIN_LEN = 5; 28 29 private long mConnectionId; 30 31 private Handler mCallback = null; 32 33 // 128 bit UUID for PBAP 34 private static final byte[] PBAP_TARGET = new byte[] { 35 0x79, 0x61, 0x35, (byte)0xf0, (byte)0xf0, (byte)0xc5, 0x11, (byte)0xd8, 0x09, 0x66, 36 0x08, 0x00, 0x20, 0x0c, (byte)0x9a, 0x66 37 }; 38 39 private static final String[] LEAGEL_PATH = { 40 "/telecom", "/telecom/pb", "/telecom/ich", "/telecom/och", "/telecom/mch", 41 "/telecom/cch", "/SIM1", "/SIM1/telecom", "/SIM1/telecom/ich", "/SIM1/telecom/och", 42 "/SIM1/telecom/mch", "/SIM1/telecom/cch", "/SIM1/telecom/pb" 43 }; 44 45 // missed call history 46 private static final String MCH = "mch"; 47 48 // incoming call history 49 private static final String ICH = "ich"; 50 51 // outgoing call history 52 private static final String OCH = "och"; 53 54 // combined call history 55 private static final String CCH = "cch"; 56 57 // phone book 58 private static final String PB = "pb"; 59 60 private static final String ICH_PATH = "/telecom/ich"; 61 62 private static final String OCH_PATH = "/telecom/och"; 63 64 private static final String MCH_PATH = "/telecom/mch"; 65 66 private static final String CCH_PATH = "/telecom/cch"; 67 68 private static final String PB_PATH = "/telecom/pb"; 69 70 // type for list vcard objects 71 private static final String TYPE_LISTING = "x-bt/vcard-listing"; 72 73 // type for get single vcard object 74 private static final String TYPE_VCARD = "x-bt/vcard"; 75 76 // type for download all vcard objects 77 private static final String TYPE_PB = "x-bt/phonebook"; 78 79 // record current path the client are browsing 80 private String mCurrentPath = ""; 81 82 // record how many missed call happens since last client operation 83 private int mMissedCallSize = 0; 84 85 private static final int NEED_PB_SIZE = 0x01; 86 87 private static final int NEED_NEW_MISSED_CALL_NUMBER = 0x02; 88 89 public static final int NEED_PHONEBOOK = 0x04; 90 91 public static final int NEED_INCOMING_CALL_NUMBER = 0x08; 92 93 public static final int NEED_OUTGOING_CALL_NUMBER = 0x10; 94 95 public static final int NEED_MISSED_CALL_NUMBER = 0x20; 96 97 public static final int NEED_COMBINED_CALL_NUMBER = 0x40; 98 99 private static final int PRECONDITION_FAILED = -1; 100 101 private static final int INTERNAL_ERROR = -2; 102 103 public BluetoothPbapObexServer(Handler callback) { 104 super(); 105 mConnectionId = -1; 106 mCallback = callback; 107 } 108 109 @Override 110 public int onConnect(final HeaderSet request, final HeaderSet reply) { 111 try { 112 byte[] uuid_tmp = (byte[])request.getHeader(HeaderSet.TARGET); 113 if (uuid_tmp.length != UUID_LENGTH) { 114 Log.w(TAG, "Wrong UUID length"); 115 return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE; 116 } 117 for (int i = 0; i < UUID_LENGTH; i++) { 118 if (uuid_tmp[i] != PBAP_TARGET[i]) { 119 Log.w(TAG, "Wrong UUID"); 120 return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE; 121 } 122 } 123 } catch (IOException e) { 124 Log.e(TAG, e.toString()); 125 return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR; 126 } 127 128 Message msg = Message.obtain(mCallback); 129 msg.what = BluetoothPbapService.MSG_SESSION_ESTABLISHED; 130 msg.sendToTarget(); 131 132 return ResponseCodes.OBEX_HTTP_OK; 133 } 134 135 @Override 136 public void onDisconnect(final HeaderSet req, final HeaderSet resp) { 137 resp.responseCode = ResponseCodes.OBEX_HTTP_OK; 138 if (mCallback != null) { 139 Message msg = Message.obtain(mCallback); 140 msg.what = BluetoothPbapService.MSG_SESSION_DISCONNECTED; 141 msg.sendToTarget(); 142 } 143 } 144 145 @Override 146 public int onPut(final Operation op) { 147 return ResponseCodes.OBEX_HTTP_BAD_REQUEST; 148 } 149 150 @Override 151 public int onSetPath(final HeaderSet request, final HeaderSet reply, final boolean backup, 152 final boolean create) { 153 String current_path_tmp = mCurrentPath; 154 String tmp_path = null; 155 try { 156 tmp_path = (String)request.getHeader(HeaderSet.NAME); 157 } catch (IOException e) { 158 Log.e(TAG, "Get name header fail"); 159 return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR; 160 } 161 if (BluetoothPbapService.DBG) { 162 Log.d(TAG, "backup=" + backup + " create=" + create); 163 } 164 if (!backup && create) { 165 if (tmp_path == null) { 166 current_path_tmp = ""; 167 } else { 168 current_path_tmp = current_path_tmp + "/" + tmp_path; 169 } 170 } 171 172 if (backup && create) { 173 if (current_path_tmp.length() != 0) { 174 current_path_tmp = current_path_tmp.substring(0, current_path_tmp.lastIndexOf("/")); 175 } 176 } 177 178 if ((current_path_tmp.length() != 0) && (!isLegalPath(current_path_tmp))) { 179 Log.w(TAG, "path is not legal"); 180 return ResponseCodes.OBEX_HTTP_BAD_REQUEST; 181 } 182 mCurrentPath = current_path_tmp; 183 return ResponseCodes.OBEX_HTTP_OK; 184 } 185 186 @Override 187 public void onClose() { 188 if (mCallback != null) { 189 Message msg = Message.obtain(mCallback); 190 msg.what = BluetoothPbapService.MSG_SERVERSESSION_CLOSE; 191 msg.sendToTarget(); 192 } 193 } 194 195 @Override 196 public int onGet(final Operation op) { 197 HeaderSet request = null; 198 HeaderSet reply = new HeaderSet(); 199 String type = ""; 200 String name = ""; 201 byte[] appParam = null; 202 AppParamValue appParamValue = new AppParamValue(); 203 try { 204 request = op.getReceivedHeader(); 205 type = (String)request.getHeader(HeaderSet.TYPE); 206 name = (String)request.getHeader(HeaderSet.NAME); 207 appParam = (byte[])request.getHeader(HeaderSet.APPLICATION_PARAMETER); 208 } catch (IOException e) { 209 Log.e(TAG, "request headers error"); 210 return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR; 211 } 212 // Accroding to specification,the name header could be omitted such as 213 // sony erriccsonHBH-DS980 214 if (BluetoothPbapService.DBG) { 215 Log.d(TAG, "OnGet type is " + type + " name is " + name); 216 } 217 if (name == null) { 218 if (BluetoothPbapService.DBG) { 219 Log.i(TAG, "name =null,guess what carkit actually want from current path"); 220 } 221 if (mCurrentPath.compareTo(PB_PATH) == 0) { 222 appParamValue.needTag |= NEED_PHONEBOOK; 223 } 224 if (mCurrentPath.compareTo(ICH_PATH) == 0) { 225 appParamValue.needTag |= NEED_INCOMING_CALL_NUMBER; 226 } 227 if (mCurrentPath.compareTo(OCH_PATH) == 0) { 228 appParamValue.needTag |= NEED_OUTGOING_CALL_NUMBER; 229 } 230 if (mCurrentPath.compareTo(CCH_PATH) == 0) { 231 appParamValue.needTag |= NEED_COMBINED_CALL_NUMBER; 232 } 233 if (mCurrentPath.compareTo(MCH_PATH) == 0) { 234 appParamValue.needTag |= NEED_MISSED_CALL_NUMBER; 235 } 236 } else {// we have weak name checking here to provide better 237 // compatibility with other devices,although unique name such as 238 // "pb.vcf" is required by SIG spec. 239 if (name.contains(PB.subSequence(0, PB.length()))) { 240 appParamValue.needTag |= NEED_PHONEBOOK; 241 if (BluetoothPbapService.DBG) { 242 Log.v(TAG, "download phonebook request"); 243 } 244 } 245 if (name.contains(ICH.subSequence(0, ICH.length()))) { 246 appParamValue.needTag |= NEED_INCOMING_CALL_NUMBER; 247 if (BluetoothPbapService.DBG) { 248 Log.v(TAG, "download incoming calls request"); 249 } 250 } 251 if (name.contains(OCH.subSequence(0, OCH.length()))) { 252 appParamValue.needTag |= NEED_OUTGOING_CALL_NUMBER; 253 if (BluetoothPbapService.DBG) { 254 Log.v(TAG, "download outgoing calls request"); 255 } 256 } 257 if (name.contains(CCH.subSequence(0, CCH.length()))) { 258 appParamValue.needTag |= NEED_COMBINED_CALL_NUMBER; 259 if (BluetoothPbapService.DBG) { 260 Log.v(TAG, "download combined calls request"); 261 } 262 } 263 if (name.contains(MCH.subSequence(0, MCH.length()))) { 264 appParamValue.needTag |= NEED_MISSED_CALL_NUMBER; 265 if (BluetoothPbapService.DBG) { 266 Log.v(TAG, "download missed calls request"); 267 } 268 } 269 } 270 // listing request 271 if (type.equals(TYPE_LISTING)) { 272 return pullVcardListing(appParam, appParamValue, reply, op); 273 } 274 // pull vcard entry request 275 else if (type.equals(TYPE_VCARD)) { 276 return pullVcardEntry(appParam, appParamValue, op, name, mCurrentPath); 277 } 278 // down load phone book request 279 else if (type.equals(TYPE_PB)) { 280 return pullPhonebook(appParam, appParamValue, reply, op, name); 281 } else { 282 Log.w(TAG, "unknown type request!!!"); 283 return ResponseCodes.OBEX_HTTP_BAD_REQUEST; 284 } 285 } // end of onGet() 286 287 /** check whether path is legal */ 288 private final boolean isLegalPath(final String str) { 289 if (str.length() == 0) { 290 return true; 291 } 292 for (int i = 0; i < LEAGAL_PATH_NUM; i++) { 293 if (str.equals(LEAGEL_PATH[i])) { 294 return true; 295 } 296 } 297 return false; 298 } 299 300 private class AppParamValue { 301 public int maxListCount; 302 303 public int listStartOffset; 304 305 public String searchValue; 306 307 public String searchAttr; 308 309 public int needTag; 310 311 public boolean vcard21; 312 313 public AppParamValue() { 314 maxListCount = 0; 315 listStartOffset = 0; 316 searchValue = ""; 317 searchAttr = ""; 318 needTag = 0x00; 319 vcard21 = true; 320 } 321 322 public void dump() { 323 Log.i(TAG, "maxListCount=" + maxListCount + " listStartOffset=" + listStartOffset 324 + " searchValue=" + searchValue + " searchAttr=" + searchAttr + " needTag=" 325 + needTag + " vcard21=" + vcard21); 326 } 327 } 328 329 /** To parse obex application parameter */ 330 private final boolean parseApplicationParameter(final byte[] appParam, 331 AppParamValue appParamValue) { 332 int i = 0; 333 boolean badRequest = true; 334 while (i < appParam.length) { 335 switch (appParam[i]) { 336 case ApplicationParameter.TRIPLET_TAGID.FILTER_TAGID: 337 i += 2; // length and tag field in triplet 338 i += ApplicationParameter.TRIPLET_LENGTH.FILTER_LENGTH; 339 break; 340 case ApplicationParameter.TRIPLET_TAGID.ORDER_TAGID: 341 i += 2; // length and tag field in triplet 342 i += ApplicationParameter.TRIPLET_LENGTH.ORDER_LENGTH; 343 break; 344 case ApplicationParameter.TRIPLET_TAGID.SEARCH_VALUE_TAGID: 345 i += 1; // length field in triplet 346 for (int k = 1; k <= appParam[i]; k++) { 347 appParamValue.searchValue += Byte.toString(appParam[i + k]); 348 } 349 // length of search value is variable 350 i += appParam[i]; 351 i += 1; 352 break; 353 case ApplicationParameter.TRIPLET_TAGID.SEARCH_ATTRIBUTE_TAGID: 354 i += 2; 355 appParamValue.searchAttr = Byte.toString(appParam[i]); 356 i += ApplicationParameter.TRIPLET_LENGTH.SEARCH_ATTRIBUTE_LENGTH; 357 break; 358 case ApplicationParameter.TRIPLET_TAGID.MAXLISTCOUNT_TAGID: 359 i += 2; 360 if (appParam[i] == 0 && appParam[i + 1] == 0) { 361 appParamValue.needTag |= NEED_PB_SIZE; 362 } else { 363 int highValue = appParam[i] & 0xff; 364 int lowValue = appParam[i + 1] & 0xff; 365 appParamValue.maxListCount = highValue * 256 + lowValue; 366 } 367 i += ApplicationParameter.TRIPLET_LENGTH.MAXLISTCOUNT_LENGTH; 368 break; 369 case ApplicationParameter.TRIPLET_TAGID.LISTSTARTOFFSET_TAGID: 370 i += 2; 371 if (appParam[i] == 0 && appParam[i + 1] == 0) { 372 appParamValue.listStartOffset = 0; 373 } else { 374 int highValue = appParam[i] & 0xff; 375 int lowValue = appParam[i + 1] & 0xff; 376 appParamValue.listStartOffset = highValue * 256 + lowValue; 377 } 378 i += ApplicationParameter.TRIPLET_LENGTH.LISTSTARTOFFSET_LENGTH; 379 break; 380 case ApplicationParameter.TRIPLET_TAGID.PHONEBOOKSIZE_TAGID: 381 i += 2;// length field in triplet 382 i += ApplicationParameter.TRIPLET_LENGTH.PHONEBOOKSIZE_LENGTH; 383 appParamValue.needTag |= NEED_PB_SIZE; 384 break; 385 case ApplicationParameter.TRIPLET_TAGID.NEWMISSEDCALLS_TAGID: 386 i += 1; 387 appParamValue.needTag |= NEED_NEW_MISSED_CALL_NUMBER; 388 i += ApplicationParameter.TRIPLET_LENGTH.NEWMISSEDCALLS_LENGTH; 389 break; 390 case ApplicationParameter.TRIPLET_TAGID.FORMAT_TAGID: 391 i += 2;// length field in triplet 392 if (Byte.toString(appParam[i]).compareTo("0") != 0) { 393 appParamValue.vcard21 = false; 394 } 395 i += ApplicationParameter.TRIPLET_LENGTH.FORMAT_LENGTH; 396 break; 397 default: 398 badRequest = false; 399 break; 400 } 401 } 402 return badRequest; 403 } 404 405 /** Form and Send an XML format String to client for Phone book listing */ 406 private final int createVcardListingXml(final int type, final Operation op, 407 final int maxListCount, final int listStartOffset, final String searchValue, 408 String searchAttr) { 409 OutputStream out = null; 410 StringBuilder result = new StringBuilder(); 411 int itemsFound = 0; 412 result.append("<?xml version=\"1.0\"?>"); 413 result.append("<!DOCTYPE vcard-listing SYSTEM \"vcard-listing.dtd\">"); 414 result.append("<vCard-listing version=\"1.0\">"); 415 416 // Phonebook listing request 417 if (type == NEED_PHONEBOOK) { 418 // if searchAttr is not set by client,make searchAttr by name 419 // as default; 420 if (searchAttr == null || searchAttr.trim().length() == 0) { 421 if (BluetoothPbapService.DBG) { 422 Log.i(TAG, "searchAttr is not set, assume search by name"); 423 } 424 searchAttr = "0"; 425 } 426 // begin of search by name 427 if (searchAttr.compareTo("0") == 0) { 428 ArrayList<String> nameList = BluetoothPbapService.getPhonebookNameList(); 429 int size = nameList.size() >= maxListCount ? maxListCount : nameList.size(); 430 if (BluetoothPbapService.DBG) { 431 Log.d(TAG, "search by name, size=" + size + " offset=" + listStartOffset 432 + " searchValue=" + searchValue); 433 } 434 // if searchValue if not set by client,provide the entire 435 // list by name 436 if (searchValue == null || searchValue.trim().length() == 0) { 437 for (int j = listStartOffset, i = 0; i < size; i++, j++) { 438 result.append("<card handle=\"" + j + ".vcf\" name=\"" + nameList.get(j) 439 + "\"" + "/>"); 440 itemsFound++; 441 } 442 } else { 443 for (int j = listStartOffset, i = 0; i < size; i++, j++) { 444 // only find the name which begins with the searchValue 445 if (nameList.get(j).indexOf(searchValue) == 0) { 446 itemsFound++; 447 result.append("<card handle=\"" + j + ".vcf\" name=\"" 448 + nameList.get(j) + "\"" + "/>"); 449 } 450 } 451 } 452 }// end of search by name 453 // begin of search by number 454 else if (searchAttr.compareTo("1") == 0) { 455 ArrayList<String> numberList = BluetoothPbapService.getPhonebookNumberList(); 456 int size = numberList.size() >= maxListCount ? maxListCount : numberList.size(); 457 if (BluetoothPbapService.DBG) { 458 Log.d(TAG, "search by number, size=" + size + " offset=" + listStartOffset 459 + " searchValue=" + searchValue); 460 } 461 // if searchValue if not set by client,provide the entire 462 // list by number 463 if (searchValue == null || searchValue.trim().length() == 0) { 464 for (int j = listStartOffset, i = 0; i < size; i++, j++) { 465 result.append("<card handle=\"" + j + ".vcf\" number=\"" 466 + numberList.get(j) + "\"" + "/>"); 467 itemsFound++; 468 } 469 } else { 470 for (int j = listStartOffset, i = 0; i < size; i++, j++) { 471 // only find the name which begins with the searchValue 472 if (numberList.get(j).indexOf(searchValue.trim()) == 0) { 473 itemsFound++; 474 result.append("<card handle=\"" + j + ".vcf\" number=\"" 475 + numberList.get(j) + "\"" + "/>"); 476 } 477 } 478 } 479 }// end of search by number 480 else { 481 return PRECONDITION_FAILED; 482 } 483 } 484 // Call history listing request 485 else { 486 ArrayList<String> nameList = BluetoothPbapService.getCallLogList(type); 487 int size = nameList.size() >= maxListCount ? maxListCount : nameList.size(); 488 if (BluetoothPbapService.DBG) { 489 Log.d(TAG, "call log list, size=" + size + " offset=" + listStartOffset); 490 } 491 // listing object begin with 1.vcf 492 for (int j = listStartOffset + 1, i = 0; i < size; i++, j++) { 493 result.append("<card handle=\"" + j + ".vcf\" name=\"" + nameList.get(j - 1) + "\"" 494 + "/>"); 495 itemsFound++; 496 } 497 } 498 result.append("</vCard-listing>"); 499 try { 500 out = op.openOutputStream(); 501 out.write(result.toString().getBytes()); 502 out.flush(); 503 } catch (IOException e) { 504 Log.e(TAG, "output stream fail " + e.toString()); 505 itemsFound = INTERNAL_ERROR; 506 } finally { 507 if (!closeStream(out, op)) { 508 itemsFound = INTERNAL_ERROR; 509 } 510 } 511 512 if (BluetoothPbapService.DBG) { 513 Log.d(TAG, "itemsFound =" + itemsFound); 514 } 515 return itemsFound; 516 } 517 518 /** 519 * Function to send obex header back to client such as get phonebook size 520 * request 521 */ 522 private final int pushHeader(final Operation op, final HeaderSet reply) { 523 OutputStream outputStream = null; 524 int ret = ResponseCodes.OBEX_HTTP_OK; 525 try { 526 op.sendHeaders(reply); 527 outputStream = op.openOutputStream(); 528 outputStream.flush(); 529 } catch (IOException e) { 530 Log.e(TAG, e.toString()); 531 ret = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR; 532 } finally { 533 if (!closeStream(outputStream, op)) { 534 ret = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR; 535 } 536 } 537 return ret; 538 } 539 540 /** Function to send vcard data to client */ 541 private final int pushBytes(final Operation op, final String string) { 542 if (string == null) { 543 return ResponseCodes.OBEX_HTTP_OK; 544 } 545 byte[] filebytes; 546 int ret = ResponseCodes.OBEX_HTTP_OK; 547 OutputStream outputStream = null; 548 if (BluetoothPbapService.DBG) { 549 Log.d(TAG, "Send Data"); 550 Log.d(TAG, string); 551 } 552 try { 553 outputStream = op.openOutputStream(); 554 filebytes = string.getBytes(); 555 outputStream.write(filebytes); 556 } catch (IOException e) { 557 Log.e(TAG, "open outputstrem failed" + e.toString()); 558 ret = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR; 559 } finally { 560 if (!closeStream(outputStream, op)) { 561 ret = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR; 562 } 563 } 564 return ret; 565 } 566 567 private final int pullVcardListing(byte[] appParam, AppParamValue appParamValue, 568 HeaderSet reply, final Operation op) { 569 String searchAttr = appParamValue.searchAttr.trim(); 570 if (!parseApplicationParameter(appParam, appParamValue)) { 571 return ResponseCodes.OBEX_HTTP_BAD_REQUEST; 572 } 573 if (BluetoothPbapService.DBG) { 574 appParamValue.dump(); 575 } 576 if (searchAttr == null || searchAttr.length() == 0) { 577 return ResponseCodes.OBEX_HTTP_PRECON_FAILED; 578 } 579 if (searchAttr.compareTo("0") != 0 && searchAttr.compareTo("1") != 0) { 580 Log.w(TAG, "search attr not supported"); 581 if (searchAttr.compareTo("2") == 0) { 582 // search by sound is not supported currently 583 Log.w(TAG, "do not support search by sound"); 584 return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED; 585 } 586 return ResponseCodes.OBEX_HTTP_PRECON_FAILED; 587 } 588 589 ApplicationParameter ap = new ApplicationParameter(); 590 if ((appParamValue.needTag & NEED_PB_SIZE) == NEED_PB_SIZE) { 591 int size = 0; 592 byte[] pbsize = new byte[2]; 593 if ((appParamValue.needTag & NEED_PHONEBOOK) == NEED_PHONEBOOK) { 594 size = BluetoothPbapService.getPhonebookSize(NEED_PHONEBOOK); 595 } else if ((appParamValue.needTag & NEED_INCOMING_CALL_NUMBER) == NEED_INCOMING_CALL_NUMBER) { 596 size = BluetoothPbapService.getPhonebookSize(NEED_INCOMING_CALL_NUMBER); 597 } else if ((appParamValue.needTag & NEED_OUTGOING_CALL_NUMBER) == NEED_OUTGOING_CALL_NUMBER) { 598 size = BluetoothPbapService.getPhonebookSize(NEED_OUTGOING_CALL_NUMBER); 599 } else if ((appParamValue.needTag & NEED_COMBINED_CALL_NUMBER) == NEED_COMBINED_CALL_NUMBER) { 600 size = BluetoothPbapService.getPhonebookSize(NEED_COMBINED_CALL_NUMBER); 601 } else if ((appParamValue.needTag & NEED_MISSED_CALL_NUMBER) == NEED_MISSED_CALL_NUMBER) { 602 size = BluetoothPbapService.getPhonebookSize(NEED_MISSED_CALL_NUMBER); 603 mMissedCallSize = size; 604 } 605 pbsize[0] = (byte)((size / 256) & 0xff);// HIGH VALUE 606 pbsize[1] = (byte)((size % 256) & 0xff);// LOW VALUE 607 ap.addAPPHeader(ApplicationParameter.TRIPLET_TAGID.PHONEBOOKSIZE_TAGID, 608 ApplicationParameter.TRIPLET_LENGTH.PHONEBOOKSIZE_LENGTH, pbsize); 609 reply.setHeader(HeaderSet.APPLICATION_PARAMETER, ap.getAPPparam()); 610 return pushHeader(op, reply); 611 } 612 613 if ((appParamValue.needTag & NEED_NEW_MISSED_CALL_NUMBER) == NEED_NEW_MISSED_CALL_NUMBER) { 614 byte[] misnum = new byte[1]; 615 int nmnum = BluetoothPbapService.getPhonebookSize(NEED_MISSED_CALL_NUMBER) 616 - mMissedCallSize; 617 nmnum = nmnum > 0 ? nmnum : 0; 618 misnum[0] = (byte)nmnum; 619 ap.addAPPHeader(ApplicationParameter.TRIPLET_TAGID.NEWMISSEDCALLS_TAGID, 620 ApplicationParameter.TRIPLET_LENGTH.NEWMISSEDCALLS_LENGTH, misnum); 621 reply.setHeader(HeaderSet.APPLICATION_PARAMETER, ap.getAPPparam()); 622 return pushHeader(op, reply); 623 } 624 625 else if ((appParamValue.needTag & NEED_PB_SIZE) == 0 626 && ((appParamValue.needTag & NEED_PHONEBOOK) == NEED_PHONEBOOK)) { 627 if (createVcardListingXml(NEED_PHONEBOOK, op, appParamValue.maxListCount, 628 appParamValue.listStartOffset, appParamValue.searchValue, 629 appParamValue.searchAttr) <= 0) { 630 return ResponseCodes.OBEX_HTTP_NOT_FOUND; 631 } 632 return ResponseCodes.OBEX_HTTP_OK; 633 } 634 635 else if ((appParamValue.needTag & NEED_PB_SIZE) == 0 636 && ((appParamValue.needTag & NEED_INCOMING_CALL_NUMBER) == NEED_INCOMING_CALL_NUMBER)) { 637 if (createVcardListingXml(NEED_INCOMING_CALL_NUMBER, op, appParamValue.maxListCount, 638 appParamValue.listStartOffset, null, null) <= 0) { 639 return ResponseCodes.OBEX_HTTP_NOT_FOUND; 640 } 641 return ResponseCodes.OBEX_HTTP_OK; 642 } 643 644 else if ((appParamValue.needTag & NEED_PB_SIZE) == 0 645 && ((appParamValue.needTag & NEED_OUTGOING_CALL_NUMBER) == NEED_OUTGOING_CALL_NUMBER)) { 646 if (createVcardListingXml(NEED_OUTGOING_CALL_NUMBER, op, appParamValue.maxListCount, 647 appParamValue.listStartOffset, null, null) <= 0) { 648 return ResponseCodes.OBEX_HTTP_NOT_FOUND; 649 } 650 return ResponseCodes.OBEX_HTTP_OK; 651 } 652 653 else if ((appParamValue.needTag & NEED_PB_SIZE) == 0 654 && ((appParamValue.needTag & NEED_COMBINED_CALL_NUMBER) == NEED_COMBINED_CALL_NUMBER)) { 655 if (createVcardListingXml(NEED_COMBINED_CALL_NUMBER, op, appParamValue.maxListCount, 656 appParamValue.listStartOffset, null, null) <= 0) { 657 return ResponseCodes.OBEX_HTTP_NOT_FOUND; 658 } 659 return ResponseCodes.OBEX_HTTP_OK; 660 } 661 662 else if ((appParamValue.needTag & NEED_PB_SIZE) == 0 663 && ((appParamValue.needTag & NEED_MISSED_CALL_NUMBER) == NEED_MISSED_CALL_NUMBER)) { 664 if (createVcardListingXml(NEED_MISSED_CALL_NUMBER, op, appParamValue.maxListCount, 665 appParamValue.listStartOffset, null, null) <= 0) { 666 return ResponseCodes.OBEX_HTTP_NOT_FOUND; 667 } 668 return ResponseCodes.OBEX_HTTP_OK; 669 } 670 return ResponseCodes.OBEX_HTTP_OK; 671 } 672 673 private final int pullVcardEntry(byte[] appParam, AppParamValue appParamValue, 674 final Operation op, final String name, final String current_path) { 675 boolean vcard21 = true; 676 if (!parseApplicationParameter(appParam, appParamValue)) 677 return ResponseCodes.OBEX_HTTP_BAD_REQUEST; 678 String str = ""; 679 String strIndex = ""; 680 int intIndex = 0; 681 if (name == null || name.length() < VCARD_NAME_MIN_LEN) { 682 return ResponseCodes.OBEX_HTTP_BAD_REQUEST; 683 } 684 strIndex = name.substring(0, name.length() - VCARD_NAME_MIN_LEN + 1); 685 if (strIndex.trim().length() != 0) { 686 try { 687 intIndex = Integer.parseInt(strIndex); 688 } catch (NumberFormatException e) { 689 Log.e(TAG, "catch number format exception " + e.toString()); 690 return ResponseCodes.OBEX_HTTP_BAD_REQUEST; 691 } 692 } 693 if (current_path.compareTo(PB_PATH) == 0) { 694 if (intIndex < 0 || intIndex >= BluetoothPbapService.getPhonebookSize(NEED_PHONEBOOK)) 695 return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE; 696 if (intIndex >= 0) 697 str = BluetoothPbapService.getPhonebook(NEED_PHONEBOOK, intIndex, vcard21); 698 } else if (current_path.compareTo(ICH_PATH) == 0) { 699 if (intIndex <= 0 700 || intIndex > BluetoothPbapService.getPhonebookSize(NEED_INCOMING_CALL_NUMBER)) 701 return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE; 702 if (intIndex >= 1) 703 str = BluetoothPbapService.getPhonebook(NEED_INCOMING_CALL_NUMBER, intIndex - 1, 704 vcard21); 705 } else if (current_path.compareTo(OCH_PATH) == 0) { 706 if (intIndex <= 0 707 || intIndex > BluetoothPbapService.getPhonebookSize(NEED_OUTGOING_CALL_NUMBER)) 708 return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE; 709 if (intIndex >= 1) 710 str = BluetoothPbapService.getPhonebook(NEED_OUTGOING_CALL_NUMBER, intIndex - 1, 711 vcard21); 712 } else if (current_path.compareTo(CCH_PATH) == 0) { 713 if (intIndex == 0) 714 return ResponseCodes.OBEX_HTTP_OK; 715 if (intIndex < 0 716 || intIndex > BluetoothPbapService.getPhonebookSize(NEED_COMBINED_CALL_NUMBER)) 717 return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE; 718 if (intIndex >= 1) 719 str = BluetoothPbapService.getPhonebook(NEED_COMBINED_CALL_NUMBER, intIndex - 1, 720 vcard21); 721 } else if (current_path.compareTo(MCH_PATH) == 0) { 722 if (intIndex <= 0 723 || intIndex > BluetoothPbapService.getPhonebookSize(NEED_MISSED_CALL_NUMBER)) 724 return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE; 725 if (intIndex >= 1) 726 str = BluetoothPbapService.getPhonebook(NEED_MISSED_CALL_NUMBER, intIndex - 1, 727 vcard21); 728 } else { 729 Log.w(TAG, "wrong path!"); 730 return ResponseCodes.OBEX_HTTP_BAD_REQUEST; 731 } 732 return pushBytes(op, str); 733 } 734 735 private final int pullPhonebook(byte[] appParam, AppParamValue appParamValue, HeaderSet reply, 736 final Operation op, final String name) { 737 boolean vcard21 = true; 738 String str; 739 String strstr = ""; 740 int pbSize = 0; 741 int size = 0; 742 int pos = 0; 743 // code start for passing PTS3.2 TC_PSE_PBD_BI_01_C 744 if (name != null) { 745 int dotIndex = name.indexOf("."); 746 String vcf = "vcf"; 747 if (dotIndex >= 0 && dotIndex <= name.length()) { 748 if (name.regionMatches(dotIndex + 1, vcf, 0, vcf.length()) == false) { 749 Log.w(TAG, "name is not .vcf"); 750 return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE; 751 } 752 } 753 } 754 // code end for passing PTS3.2 TC_PSE_PBD_BI_01_C 755 if (!parseApplicationParameter(appParam, appParamValue)) { 756 return ResponseCodes.OBEX_HTTP_BAD_REQUEST; 757 } 758 // check whether to send application response parameter to client 759 ApplicationParameter ap = new ApplicationParameter(); 760 if ((appParamValue.needTag & NEED_PB_SIZE) == NEED_PB_SIZE) { 761 byte[] pbsize = new byte[2]; 762 if ((appParamValue.needTag & NEED_PHONEBOOK) == NEED_PHONEBOOK) { 763 size = BluetoothPbapService.getPhonebookSize(NEED_PHONEBOOK); 764 } else if ((appParamValue.needTag & NEED_INCOMING_CALL_NUMBER) == NEED_INCOMING_CALL_NUMBER) { 765 size = BluetoothPbapService.getPhonebookSize(NEED_INCOMING_CALL_NUMBER); 766 } else if ((appParamValue.needTag & NEED_OUTGOING_CALL_NUMBER) == NEED_OUTGOING_CALL_NUMBER) { 767 size = BluetoothPbapService.getPhonebookSize(NEED_OUTGOING_CALL_NUMBER); 768 } else if ((appParamValue.needTag & NEED_COMBINED_CALL_NUMBER) == NEED_COMBINED_CALL_NUMBER) { 769 size = BluetoothPbapService.getPhonebookSize(NEED_COMBINED_CALL_NUMBER); 770 } else if ((appParamValue.needTag & NEED_MISSED_CALL_NUMBER) == NEED_MISSED_CALL_NUMBER) { 771 size = BluetoothPbapService.getPhonebookSize(NEED_MISSED_CALL_NUMBER); 772 mMissedCallSize = size; 773 } 774 pbsize[0] = (byte)((size / 256) & 0xff);// HIGH VALUE 775 pbsize[1] = (byte)((size % 256) & 0xff);// LOW VALUE 776 ap.addAPPHeader(ApplicationParameter.TRIPLET_TAGID.PHONEBOOKSIZE_TAGID, 777 ApplicationParameter.TRIPLET_LENGTH.PHONEBOOKSIZE_LENGTH, pbsize); 778 reply.setHeader(HeaderSet.APPLICATION_PARAMETER, ap.getAPPparam()); 779 return pushHeader(op, reply); 780 } 781 782 if ((appParamValue.needTag & NEED_NEW_MISSED_CALL_NUMBER) == NEED_NEW_MISSED_CALL_NUMBER) { 783 byte[] misnum = new byte[1]; 784 int nmnum = BluetoothPbapService.getPhonebookSize(NEED_MISSED_CALL_NUMBER) 785 - mMissedCallSize; 786 nmnum = nmnum > 0 ? nmnum : 0; 787 misnum[0] = (byte)nmnum; 788 ap.addAPPHeader(ApplicationParameter.TRIPLET_TAGID.NEWMISSEDCALLS_TAGID, 789 ApplicationParameter.TRIPLET_LENGTH.NEWMISSEDCALLS_LENGTH, misnum); 790 reply.setHeader(HeaderSet.APPLICATION_PARAMETER, ap.getAPPparam()); 791 return pushHeader(op, reply); 792 } 793 794 if ((appParamValue.needTag & NEED_PHONEBOOK) == NEED_PHONEBOOK) { 795 pbSize = BluetoothPbapService.getPhonebookSize(NEED_PHONEBOOK); 796 size = pbSize >= appParamValue.maxListCount ? appParamValue.maxListCount : pbSize; 797 for (pos = appParamValue.listStartOffset; pos < size; pos++) { 798 str = BluetoothPbapService.getPhonebook(NEED_PHONEBOOK, pos, vcard21); 799 strstr += str; 800 } 801 } 802 803 if ((appParamValue.needTag & NEED_INCOMING_CALL_NUMBER) == NEED_INCOMING_CALL_NUMBER) { 804 pbSize = BluetoothPbapService.getPhonebookSize(NEED_INCOMING_CALL_NUMBER); 805 size = pbSize >= appParamValue.maxListCount ? appParamValue.maxListCount : pbSize; 806 for (pos = appParamValue.listStartOffset; pos < size; pos++) { 807 str = BluetoothPbapService.getPhonebook(NEED_INCOMING_CALL_NUMBER, pos, vcard21); 808 strstr += str; 809 } 810 } 811 812 if ((appParamValue.needTag & NEED_OUTGOING_CALL_NUMBER) == NEED_OUTGOING_CALL_NUMBER) { 813 pbSize = BluetoothPbapService.getPhonebookSize(NEED_OUTGOING_CALL_NUMBER); 814 size = pbSize >= appParamValue.maxListCount ? appParamValue.maxListCount : pbSize; 815 for (pos = appParamValue.listStartOffset; pos < size; pos++) { 816 str = BluetoothPbapService.getPhonebook(NEED_OUTGOING_CALL_NUMBER, pos, vcard21); 817 strstr += str; 818 } 819 } 820 821 if ((appParamValue.needTag & NEED_COMBINED_CALL_NUMBER) == NEED_COMBINED_CALL_NUMBER) { 822 pbSize = BluetoothPbapService.getPhonebookSize(NEED_COMBINED_CALL_NUMBER); 823 size = pbSize >= appParamValue.maxListCount ? appParamValue.maxListCount : pbSize; 824 for (pos = appParamValue.listStartOffset; pos < size; pos++) { 825 str = BluetoothPbapService.getPhonebook(NEED_COMBINED_CALL_NUMBER, pos, vcard21); 826 strstr += str; 827 } 828 } 829 830 if ((appParamValue.needTag & NEED_MISSED_CALL_NUMBER) == NEED_MISSED_CALL_NUMBER) { 831 pbSize = BluetoothPbapService.getPhonebookSize(NEED_MISSED_CALL_NUMBER); 832 size = pbSize >= appParamValue.maxListCount ? appParamValue.maxListCount : pbSize; 833 for (pos = appParamValue.listStartOffset; pos < size; pos++) { 834 str = BluetoothPbapService.getPhonebook(NEED_MISSED_CALL_NUMBER, pos, vcard21); 835 strstr += str; 836 } 837 } 838 return pushBytes(op, strstr); 839 } 840 841 private boolean closeStream(final OutputStream out, final Operation op) { 842 boolean returnvalue = true; 843 try { 844 if (out != null) { 845 out.close(); 846 } 847 } catch (IOException e) { 848 Log.e(TAG, "outputStream close failed" + e.toString()); 849 returnvalue = false; 850 } 851 try { 852 if (op != null) { 853 op.close(); 854 } 855 } catch (IOException e) { 856 Log.e(TAG, "oeration close failed" + e.toString()); 857 returnvalue = false; 858 } 859 return returnvalue; 860 } 861 862 public final void setConnectionID(final long id) { 863 if ((id < -1) || (id > 0xFFFFFFFFL)) { 864 throw new IllegalArgumentException("Illegal Connection ID"); 865 } 866 mConnectionId = id; 867 } 868 869 /** 870 * Retrieves the connection ID that is being used in the present connection. 871 * This method will return -1 if no connection ID is being used. 872 * 873 * @return the connection id being used or -1 if no connection ID is being 874 * used 875 */ 876 public final long getConnectionID() { 877 return mConnectionId; 878 } 879 880 // Reserved for future use.In case PSE challenge PCE and PCE input wrong 881 // session key. 882 public final void onAuthenticationFailure(final byte[] userName) { 883 } 884} 885