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