1 /* 2 * Copyright (C) 2015 Samsung System LSI 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 package com.android.bluetooth.sdp; 16 17 import android.bluetooth.BluetoothDevice; 18 import android.bluetooth.SdpMasRecord; 19 import android.bluetooth.SdpMnsRecord; 20 import android.bluetooth.SdpOppOpsRecord; 21 import android.bluetooth.SdpPseRecord; 22 import android.bluetooth.SdpSapsRecord; 23 import android.bluetooth.SdpRecord; 24 import android.content.Intent; 25 import android.os.Handler; 26 import android.os.Message; 27 import android.os.ParcelUuid; 28 import android.os.Parcelable; 29 import android.util.Log; 30 31 import com.android.bluetooth.Utils; 32 import com.android.bluetooth.btservice.AbstractionLayer; 33 import com.android.bluetooth.btservice.AdapterService; 34 35 import java.util.ArrayList; 36 import java.util.Arrays; 37 38 public class SdpManager { 39 40 private static final boolean D = true; 41 private static final boolean V = false; 42 private static final String TAG="SdpManager"; 43 44 // TODO: When changing PBAP to use this new API. 45 // Move the defines to the profile (PBAP already have the feature bits) 46 /* PBAP repositories */ 47 public static final byte PBAP_REPO_LOCAL = 0x01<<0; 48 public static final byte PBAP_REPO_SIM = 0x01<<1; 49 public static final byte PBAP_REPO_SPEED_DAIL = 0x01<<2; 50 public static final byte PBAP_REPO_FAVORITES = 0x01<<3; 51 52 // TODO: When changing OPP to use this new API. 53 // Move the defines to the profile 54 /* Object Push formats */ 55 public static final byte OPP_FORMAT_VCARD21 = 0x01; 56 public static final byte OPP_FORMAT_VCARD30 = 0x02; 57 public static final byte OPP_FORMAT_VCAL10 = 0x03; 58 public static final byte OPP_FORMAT_ICAL20 = 0x04; 59 public static final byte OPP_FORMAT_VNOTE = 0x05; 60 public static final byte OPP_FORMAT_VMESSAGE = 0x06; 61 public static final byte OPP_FORMAT_ANY_TYPE_OF_OBJ = (byte)0xFF; 62 63 public static final byte[] OPP_FORMAT_ALL= { 64 OPP_FORMAT_VCARD21, 65 OPP_FORMAT_VCARD30, 66 OPP_FORMAT_VCAL10, 67 OPP_FORMAT_ICAL20, 68 OPP_FORMAT_VNOTE, 69 OPP_FORMAT_VMESSAGE, 70 OPP_FORMAT_ANY_TYPE_OF_OBJ}; 71 72 /* Variables to keep track of ongoing and queued search requests. 73 * mTrackerLock must be held, when using/changing sSdpSearchTracker 74 * and mSearchInProgress. */ 75 static SdpSearchTracker sSdpSearchTracker; 76 static boolean mSearchInProgress = false; 77 static Object mTrackerLock = new Object(); 78 79 /* The timeout to wait for reply from native. Should never fire. */ 80 private static final int SDP_INTENT_DELAY = 6000; 81 private static final int MESSAGE_SDP_INTENT = 2; 82 83 // We need a reference to the adapter service, to be able to send intents 84 private static AdapterService sAdapterService; 85 private static boolean sNativeAvailable; 86 87 // This object is a singleton 88 private static SdpManager sSdpManager = null; 89 90 static { classInitNative()91 classInitNative(); 92 } 93 classInitNative()94 private native static void classInitNative(); initializeNative()95 private native void initializeNative(); cleanupNative()96 private native void cleanupNative(); sdpSearchNative(byte[] address, byte[] uuid)97 private native boolean sdpSearchNative(byte[] address, byte[] uuid); 98 sdpCreateMapMasRecordNative(String serviceName, int masId, int rfcommChannel, int l2capPsm, int version, int msgTypes, int features)99 private native int sdpCreateMapMasRecordNative(String serviceName, int masId, 100 int rfcommChannel, int l2capPsm, int version, int msgTypes, int features); 101 sdpCreateMapMnsRecordNative(String serviceName, int rfcommChannel, int l2capPsm, int version, int features)102 private native int sdpCreateMapMnsRecordNative(String serviceName, 103 int rfcommChannel, int l2capPsm, int version, int features); 104 sdpCreatePbapPseRecordNative(String serviceName, int rfcommChannel, int l2capPsm, int version, int repositories, int features)105 private native int sdpCreatePbapPseRecordNative(String serviceName, int rfcommChannel, 106 int l2capPsm, int version, int repositories, int features); 107 sdpCreateOppOpsRecordNative(String serviceName, int rfcommChannel, int l2capPsm, int version, byte[] formats_list)108 private native int sdpCreateOppOpsRecordNative(String serviceName, 109 int rfcommChannel, int l2capPsm, int version, byte[] formats_list); 110 sdpCreateSapsRecordNative(String serviceName, int rfcommChannel, int version)111 private native int sdpCreateSapsRecordNative(String serviceName, int rfcommChannel, 112 int version); 113 sdpRemoveSdpRecordNative(int record_id)114 private native boolean sdpRemoveSdpRecordNative(int record_id); 115 116 117 /* Inner class used for wrapping sdp search instance data */ 118 private class SdpSearchInstance { 119 private final BluetoothDevice mDevice; 120 private final ParcelUuid mUuid; 121 private int mStatus = 0; 122 private boolean mSearching; 123 /* TODO: If we change the API to use another mechanism than intents for 124 * delivering the results, this would be the place to keep a list 125 * of the objects to deliver the results to. */ SdpSearchInstance(int status, BluetoothDevice device, ParcelUuid uuid)126 public SdpSearchInstance(int status, BluetoothDevice device, ParcelUuid uuid){ 127 this.mDevice = device; 128 this.mUuid = uuid; 129 this.mStatus = status; 130 mSearching = true; 131 } getDevice()132 public BluetoothDevice getDevice() { 133 return mDevice; 134 } getUuid()135 public ParcelUuid getUuid() { 136 return mUuid; 137 } getStatus()138 public int getStatus(){ 139 return mStatus; 140 } 141 setStatus(int status)142 public void setStatus(int status) { 143 this.mStatus = status; 144 } 145 startSearch()146 public void startSearch() { 147 mSearching = true; 148 Message message = mHandler.obtainMessage(MESSAGE_SDP_INTENT, this); 149 mHandler.sendMessageDelayed(message, SDP_INTENT_DELAY); 150 } 151 stopSearch()152 public void stopSearch() { 153 if(mSearching) { 154 mHandler.removeMessages(MESSAGE_SDP_INTENT, this); 155 } 156 mSearching = false; 157 } isSearching()158 public boolean isSearching() { 159 return mSearching; 160 } 161 } 162 163 164 /* We wrap the ArrayList class to decorate with functionality to 165 * find an instance based on UUID AND device address. 166 * As we use a mix of byte[] and object instances, this is more 167 * efficient than implementing comparable. */ 168 class SdpSearchTracker { 169 private final ArrayList<SdpSearchInstance> list = new ArrayList<SdpSearchInstance>(); 170 clear()171 void clear() { 172 list.clear(); 173 } 174 add(SdpSearchInstance inst)175 boolean add(SdpSearchInstance inst){ 176 return list.add(inst); 177 } 178 remove(SdpSearchInstance inst)179 boolean remove(SdpSearchInstance inst) { 180 return list.remove(inst); 181 } 182 getNext()183 SdpSearchInstance getNext() { 184 if(list.size() > 0) { 185 return list.get(0); 186 } 187 return null; 188 } 189 getSearchInstance(byte[] address, byte[] uuidBytes)190 SdpSearchInstance getSearchInstance(byte[] address, byte[] uuidBytes) { 191 String addressString = Utils.getAddressStringFromByte(address); 192 ParcelUuid uuid = Utils.byteArrayToUuid(uuidBytes)[0]; 193 for (SdpSearchInstance inst : list) { 194 if (inst.getDevice().getAddress().equals(addressString) 195 && inst.getUuid().equals(uuid)) { 196 return inst; 197 } 198 } 199 return null; 200 } 201 isSearching(BluetoothDevice device, ParcelUuid uuid)202 boolean isSearching(BluetoothDevice device, ParcelUuid uuid) { 203 String addressString = device.getAddress(); 204 for (SdpSearchInstance inst : list) { 205 if (inst.getDevice().getAddress().equals(addressString) 206 && inst.getUuid().equals(uuid)) { 207 return inst.isSearching(); 208 } 209 } 210 return false; 211 } 212 } 213 214 SdpManager(AdapterService adapterService)215 private SdpManager(AdapterService adapterService) { 216 sSdpSearchTracker = new SdpSearchTracker(); 217 218 /* This is only needed until intents are no longer used */ 219 sAdapterService = adapterService; 220 initializeNative(); 221 sNativeAvailable=true; 222 } 223 224 init(AdapterService adapterService)225 public static SdpManager init(AdapterService adapterService) { 226 sSdpManager = new SdpManager(adapterService); 227 return sSdpManager; 228 } 229 getDefaultManager()230 public static SdpManager getDefaultManager() { 231 return sSdpManager; 232 } 233 cleanup()234 public void cleanup() { 235 if (sSdpSearchTracker !=null) { 236 synchronized(mTrackerLock) { 237 sSdpSearchTracker.clear(); 238 } 239 } 240 241 if (sNativeAvailable) { 242 cleanupNative(); 243 sNativeAvailable=false; 244 } 245 sSdpManager = null; 246 } 247 248 sdpMasRecordFoundCallback(int status, byte[] address, byte[] uuid, int masInstanceId, int l2capPsm, int rfcommCannelNumber, int profileVersion, int supportedFeatures, int supportedMessageTypes, String serviceName, boolean moreResults)249 void sdpMasRecordFoundCallback(int status, byte[] address, byte[] uuid, 250 int masInstanceId, 251 int l2capPsm, 252 int rfcommCannelNumber, 253 int profileVersion, 254 int supportedFeatures, 255 int supportedMessageTypes, 256 String serviceName, 257 boolean moreResults) { 258 259 synchronized(mTrackerLock) { 260 SdpSearchInstance inst = sSdpSearchTracker.getSearchInstance(address, uuid); 261 SdpMasRecord sdpRecord = null; 262 if (inst == null) { 263 Log.e(TAG, "sdpRecordFoundCallback: Search instance is NULL"); 264 return; 265 } 266 inst.setStatus(status); 267 if(status == AbstractionLayer.BT_STATUS_SUCCESS) { 268 sdpRecord = new SdpMasRecord(masInstanceId, 269 l2capPsm, 270 rfcommCannelNumber, 271 profileVersion, 272 supportedFeatures, 273 supportedMessageTypes, 274 serviceName); 275 } 276 if(D) Log.d(TAG, "UUID: " + Arrays.toString(uuid)); 277 if(D) Log.d(TAG, "UUID in parcel: " + ((Utils.byteArrayToUuid(uuid))[0]).toString()); 278 sendSdpIntent(inst, sdpRecord, moreResults); 279 } 280 } 281 sdpMnsRecordFoundCallback(int status, byte[] address, byte[] uuid, int l2capPsm, int rfcommCannelNumber, int profileVersion, int supportedFeatures, String serviceName, boolean moreResults)282 void sdpMnsRecordFoundCallback(int status, byte[] address, byte[] uuid, 283 int l2capPsm, 284 int rfcommCannelNumber, 285 int profileVersion, 286 int supportedFeatures, 287 String serviceName, 288 boolean moreResults) { 289 synchronized(mTrackerLock) { 290 291 SdpSearchInstance inst = sSdpSearchTracker.getSearchInstance(address, uuid); 292 SdpMnsRecord sdpRecord = null; 293 if (inst == null) { 294 Log.e(TAG, "sdpRecordFoundCallback: Search instance is NULL"); 295 return; 296 } 297 inst.setStatus(status); 298 if(status == AbstractionLayer.BT_STATUS_SUCCESS) { 299 sdpRecord = new SdpMnsRecord(l2capPsm, 300 rfcommCannelNumber, 301 profileVersion, 302 supportedFeatures, 303 serviceName); 304 } 305 if(D) Log.d(TAG, "UUID: " + Arrays.toString(uuid)); 306 if(D) Log.d(TAG, "UUID in parcel: " + ((Utils.byteArrayToUuid(uuid))[0]).toString()); 307 sendSdpIntent(inst, sdpRecord, moreResults); 308 } 309 } 310 sdpPseRecordFoundCallback(int status, byte[] address, byte[] uuid, int l2capPsm, int rfcommCannelNumber, int profileVersion, int supportedFeatures, int supportedRepositories, String serviceName, boolean moreResults)311 void sdpPseRecordFoundCallback(int status, byte[] address, byte[] uuid, 312 int l2capPsm, 313 int rfcommCannelNumber, 314 int profileVersion, 315 int supportedFeatures, 316 int supportedRepositories, 317 String serviceName, 318 boolean moreResults) { 319 synchronized(mTrackerLock) { 320 SdpSearchInstance inst = sSdpSearchTracker.getSearchInstance(address, uuid); 321 SdpPseRecord sdpRecord = null; 322 if (inst == null) { 323 Log.e(TAG, "sdpRecordFoundCallback: Search instance is NULL"); 324 return; 325 } 326 inst.setStatus(status); 327 if(status == AbstractionLayer.BT_STATUS_SUCCESS) { 328 sdpRecord = new SdpPseRecord(l2capPsm, 329 rfcommCannelNumber, 330 profileVersion, 331 supportedFeatures, 332 supportedRepositories, 333 serviceName); 334 } 335 if(D) Log.d(TAG, "UUID: " + Arrays.toString(uuid)); 336 if(D) Log.d(TAG, "UUID in parcel: " + ((Utils.byteArrayToUuid(uuid))[0]).toString()); 337 sendSdpIntent(inst, sdpRecord, moreResults); 338 } 339 } 340 sdpOppOpsRecordFoundCallback(int status, byte[] address, byte[] uuid, int l2capPsm, int rfcommCannelNumber, int profileVersion, String serviceName, byte[] formatsList, boolean moreResults)341 void sdpOppOpsRecordFoundCallback(int status, byte[] address, byte[] uuid, 342 int l2capPsm, 343 int rfcommCannelNumber, 344 int profileVersion, 345 String serviceName, 346 byte[] formatsList, 347 boolean moreResults) { 348 349 synchronized(mTrackerLock) { 350 SdpSearchInstance inst = sSdpSearchTracker.getSearchInstance(address, uuid); 351 SdpOppOpsRecord sdpRecord = null; 352 353 if (inst == null) { 354 Log.e(TAG, "sdpOppOpsRecordFoundCallback: Search instance is NULL"); 355 return; 356 } 357 inst.setStatus(status); 358 if(status == AbstractionLayer.BT_STATUS_SUCCESS) { 359 sdpRecord = new SdpOppOpsRecord(serviceName, 360 rfcommCannelNumber, 361 l2capPsm, 362 profileVersion, 363 formatsList); 364 } 365 if(D) Log.d(TAG, "UUID: " + Arrays.toString(uuid)); 366 if(D) Log.d(TAG, "UUID in parcel: " + ((Utils.byteArrayToUuid(uuid))[0]).toString()); 367 sendSdpIntent(inst, sdpRecord, moreResults); 368 } 369 } 370 sdpSapsRecordFoundCallback(int status, byte[] address, byte[] uuid, int rfcommCannelNumber, int profileVersion, String serviceName, boolean moreResults)371 void sdpSapsRecordFoundCallback(int status, byte[] address, byte[] uuid, 372 int rfcommCannelNumber, 373 int profileVersion, 374 String serviceName, 375 boolean moreResults) { 376 377 synchronized(mTrackerLock) { 378 SdpSearchInstance inst = sSdpSearchTracker.getSearchInstance(address, uuid); 379 SdpSapsRecord sdpRecord = null; 380 if (inst == null) { 381 Log.e(TAG, "sdpSapsRecordFoundCallback: Search instance is NULL"); 382 return; 383 } 384 inst.setStatus(status); 385 if (status == AbstractionLayer.BT_STATUS_SUCCESS) { 386 sdpRecord = new SdpSapsRecord(rfcommCannelNumber, 387 profileVersion, 388 serviceName); 389 } 390 if (D) Log.d(TAG, "UUID: " + Arrays.toString(uuid)); 391 if (D) Log.d(TAG, "UUID in parcel: " + ((Utils.byteArrayToUuid(uuid))[0]).toString()); 392 sendSdpIntent(inst, sdpRecord, moreResults); 393 } 394 } 395 396 /* TODO: Test or remove! */ sdpRecordFoundCallback(int status, byte[] address, byte[] uuid, int size_record, byte[] record)397 void sdpRecordFoundCallback(int status, byte[] address, byte[] uuid, 398 int size_record, byte[] record) { 399 synchronized(mTrackerLock) { 400 401 SdpSearchInstance inst = sSdpSearchTracker.getSearchInstance(address, uuid); 402 SdpRecord sdpRecord = null; 403 if (inst == null) { 404 Log.e(TAG, "sdpRecordFoundCallback: Search instance is NULL"); 405 return; 406 } 407 inst.setStatus(status); 408 if(status == AbstractionLayer.BT_STATUS_SUCCESS) { 409 if(D) Log.d(TAG, "sdpRecordFoundCallback: found a sdp record of size " 410 + size_record ); 411 if(D) Log.d(TAG, "Record:"+ Arrays.toString(record)); 412 sdpRecord = new SdpRecord(size_record, record); 413 } 414 if(D) Log.d(TAG, "UUID: " + Arrays.toString(uuid)); 415 if(D) Log.d(TAG, "UUID in parcel: " + ((Utils.byteArrayToUuid(uuid))[0]).toString()); 416 sendSdpIntent(inst, sdpRecord, false); 417 } 418 } 419 sdpSearch(BluetoothDevice device, ParcelUuid uuid)420 public void sdpSearch(BluetoothDevice device, ParcelUuid uuid) { 421 if (sNativeAvailable == false) { 422 Log.e(TAG, "Native not initialized!"); 423 return; 424 } 425 synchronized (mTrackerLock) { 426 if (sSdpSearchTracker.isSearching(device, uuid)) { 427 /* Search already in progress */ 428 return; 429 } 430 431 SdpSearchInstance inst = new SdpSearchInstance(0, device, uuid); 432 sSdpSearchTracker.add(inst); // Queue the request 433 434 startSearch(); // Start search if not busy 435 } 436 437 } 438 439 /* Caller must hold the mTrackerLock */ startSearch()440 private void startSearch() { 441 442 SdpSearchInstance inst = sSdpSearchTracker.getNext(); 443 444 if((inst != null) && (mSearchInProgress == false)) { 445 if(D) Log.d(TAG, "Starting search for UUID: "+ inst.getUuid()); 446 mSearchInProgress = true; 447 448 inst.startSearch(); // Trigger timeout message 449 450 sdpSearchNative(Utils.getBytesFromAddress(inst.getDevice().getAddress()), 451 Utils.uuidToByteArray(inst.getUuid())); 452 } // Else queue is empty. 453 else { 454 if(D) Log.d(TAG, "startSearch(): nextInst = " + inst + 455 " mSearchInProgress = " + mSearchInProgress 456 + " - search busy or queue empty."); 457 } 458 } 459 460 /* Caller must hold the mTrackerLock */ sendSdpIntent(SdpSearchInstance inst, Parcelable record, boolean moreResults)461 private void sendSdpIntent(SdpSearchInstance inst, 462 Parcelable record, boolean moreResults) { 463 464 inst.stopSearch(); 465 466 Intent intent = new Intent(BluetoothDevice.ACTION_SDP_RECORD); 467 468 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, inst.getDevice()); 469 intent.putExtra(BluetoothDevice.EXTRA_SDP_SEARCH_STATUS, inst.getStatus()); 470 if (record != null) intent.putExtra(BluetoothDevice.EXTRA_SDP_RECORD, record); 471 intent.putExtra(BluetoothDevice.EXTRA_UUID, inst.getUuid()); 472 /* TODO: BLUETOOTH_ADMIN_PERM was private... change to callback interface. 473 * Keep in mind that the MAP client needs to use this as well, 474 * hence to make it call-backs, the MAP client profile needs to be 475 * part of the Bluetooth APK. */ 476 sAdapterService.sendBroadcast(intent, AdapterService.BLUETOOTH_ADMIN_PERM); 477 478 if(moreResults == false) { 479 //Remove the outstanding UUID request 480 sSdpSearchTracker.remove(inst); 481 mSearchInProgress = false; 482 startSearch(); 483 } 484 } 485 486 private final Handler mHandler = new Handler() { 487 @Override 488 public void handleMessage(Message msg) { 489 switch (msg.what) { 490 case MESSAGE_SDP_INTENT: 491 SdpSearchInstance msgObj = (SdpSearchInstance)msg.obj; 492 Log.w(TAG, "Search timedout for UUID " + msgObj.getUuid()); 493 synchronized (mTrackerLock) { 494 sendSdpIntent(msgObj, null, false); 495 } 496 break; 497 } 498 } 499 }; 500 501 /** 502 * Create a server side Message Access Profile Service Record. 503 * Create the record once, and reuse it for all connections. 504 * If changes to a record is needed remove the old record using {@link removeSdpRecord} 505 * and then create a new one. 506 * @param serviceName The textual name of the service 507 * @param masId The MAS ID to associate with this SDP record 508 * @param rfcommChannel The RFCOMM channel that clients can connect to 509 * (obtain from BluetoothServerSocket) 510 * @param l2capPsm The L2CAP PSM channel that clients can connect to 511 * (obtain from BluetoothServerSocket) 512 * Supply -1 to omit the L2CAP PSM from the record. 513 * @param version The Profile version number (As specified in the Bluetooth 514 * MAP specification) 515 * @param msgTypes The supported message types bit mask (As specified in 516 * the Bluetooth MAP specification) 517 * @param features The feature bit mask (As specified in the Bluetooth 518 * MAP specification) 519 * @return a handle to the record created. The record can be removed again 520 * using {@link removeSdpRecord}(). The record is not linked to the 521 * creation/destruction of BluetoothSockets, hence SDP record cleanup 522 * is a separate process. 523 */ createMapMasRecord(String serviceName, int masId, int rfcommChannel, int l2capPsm, int version, int msgTypes, int features)524 public int createMapMasRecord(String serviceName, int masId, 525 int rfcommChannel, int l2capPsm, int version, 526 int msgTypes, int features) { 527 if(sNativeAvailable == false) { 528 throw new RuntimeException(TAG + " sNativeAvailable == false - native not initialized"); 529 } 530 return sdpCreateMapMasRecordNative(serviceName, masId, rfcommChannel, 531 l2capPsm, version, msgTypes, features); 532 } 533 534 /** 535 * Create a client side Message Access Profile Service Record. 536 * Create the record once, and reuse it for all connections. 537 * If changes to a record is needed remove the old record using {@link removeSdpRecord} 538 * and then create a new one. 539 * @param serviceName The textual name of the service 540 * @param rfcommChannel The RFCOMM channel that clients can connect to 541 * (obtain from BluetoothServerSocket) 542 * @param l2capPsm The L2CAP PSM channel that clients can connect to 543 * (obtain from BluetoothServerSocket) 544 * Supply -1 to omit the L2CAP PSM from the record. 545 * @param version The Profile version number (As specified in the Bluetooth 546 * MAP specification) 547 * @param features The feature bit mask (As specified in the Bluetooth 548 * MAP specification) 549 * @return a handle to the record created. The record can be removed again 550 * using {@link removeSdpRecord}(). The record is not linked to the 551 * creation/destruction of BluetoothSockets, hence SDP record cleanup 552 * is a separate process. 553 */ createMapMnsRecord(String serviceName, int rfcommChannel, int l2capPsm, int version, int features)554 public int createMapMnsRecord(String serviceName, int rfcommChannel, 555 int l2capPsm, int version, int features) { 556 if(sNativeAvailable == false) { 557 throw new RuntimeException(TAG + " sNativeAvailable == false - native not initialized"); 558 } 559 return sdpCreateMapMnsRecordNative(serviceName, rfcommChannel, 560 l2capPsm, version, features); 561 } 562 563 /** 564 * Create a Server side Phone Book Access Profile Service Record. 565 * Create the record once, and reuse it for all connections. 566 * If changes to a record is needed remove the old record using {@link removeSdpRecord} 567 * and then create a new one. 568 * @param serviceName The textual name of the service 569 * @param rfcommChannel The RFCOMM channel that clients can connect to 570 * (obtain from BluetoothServerSocket) 571 * @param l2capPsm The L2CAP PSM channel that clients can connect to 572 * (obtain from BluetoothServerSocket) 573 * Supply -1 to omit the L2CAP PSM from the record. 574 * @param version The Profile version number (As specified in the Bluetooth 575 * PBAP specification) 576 * @param repositories The supported repositories bit mask (As specified in 577 * the Bluetooth PBAP specification) 578 * @param features The feature bit mask (As specified in the Bluetooth 579 * PBAP specification) 580 * @return a handle to the record created. The record can be removed again 581 * using {@link removeSdpRecord}(). The record is not linked to the 582 * creation/destruction of BluetoothSockets, hence SDP record cleanup 583 * is a separate process. 584 */ createPbapPseRecord(String serviceName, int rfcommChannel, int l2capPsm, int version, int repositories, int features)585 public int createPbapPseRecord(String serviceName, int rfcommChannel, int l2capPsm, 586 int version, int repositories, int features) { 587 if(sNativeAvailable == false) { 588 throw new RuntimeException(TAG + " sNativeAvailable == false - native not initialized"); 589 } 590 return sdpCreatePbapPseRecordNative(serviceName, rfcommChannel, 591 l2capPsm, version, repositories, features); 592 } 593 594 /** 595 * Create a Server side Object Push Profile Service Record. 596 * Create the record once, and reuse it for all connections. 597 * If changes to a record is needed remove the old record using {@link removeSdpRecord} 598 * and then create a new one. 599 * @param serviceName The textual name of the service 600 * @param rfcommChannel The RFCOMM channel that clients can connect to 601 * (obtain from BluetoothServerSocket) 602 * @param l2capPsm The L2CAP PSM channel that clients can connect to 603 * (obtain from BluetoothServerSocket) 604 * Supply -1 to omit the L2CAP PSM from the record. 605 * @param version The Profile version number (As specified in the Bluetooth 606 * OPP specification) 607 * @param formatsList A list of the supported formats (As specified in 608 * the Bluetooth OPP specification) 609 * @return a handle to the record created. The record can be removed again 610 * using {@link removeSdpRecord}(). The record is not linked to the 611 * creation/destruction of BluetoothSockets, hence SDP record cleanup 612 * is a separate process. 613 */ createOppOpsRecord(String serviceName, int rfcommChannel, int l2capPsm, int version, byte[] formatsList)614 public int createOppOpsRecord(String serviceName, int rfcommChannel, int l2capPsm, 615 int version, byte[] formatsList) { 616 if(sNativeAvailable == false) { 617 throw new RuntimeException(TAG + " sNativeAvailable == false - native not initialized"); 618 } 619 return sdpCreateOppOpsRecordNative(serviceName, rfcommChannel, 620 l2capPsm, version, formatsList); 621 } 622 623 /** 624 * Create a server side Sim Access Profile Service Record. 625 * Create the record once, and reuse it for all connections. 626 * If changes to a record is needed remove the old record using {@link removeSdpRecord} 627 * and then create a new one. 628 * @param serviceName The textual name of the service 629 * @param rfcommChannel The RFCOMM channel that clients can connect to 630 * (obtain from BluetoothServerSocket) 631 * @param version The Profile version number (As specified in the Bluetooth 632 * SAP specification) 633 * @return a handle to the record created. The record can be removed again 634 * using {@link removeSdpRecord}(). The record is not linked to the 635 * creation/destruction of BluetoothSockets, hence SDP record cleanup 636 * is a separate process. 637 */ createSapsRecord(String serviceName, int rfcommChannel, int version)638 public int createSapsRecord(String serviceName, int rfcommChannel, int version) { 639 if (sNativeAvailable == false) { 640 throw new RuntimeException(TAG + " sNativeAvailable == false - native not initialized"); 641 } 642 return sdpCreateSapsRecordNative(serviceName, rfcommChannel, version); 643 } 644 645 /** 646 * Remove a SDP record. 647 * When Bluetooth is disabled all records will be deleted, hence there 648 * is no need to call this function when bluetooth is disabled. 649 * @param recordId The Id returned by on of the createXxxXxxRecord() functions. 650 * @return TRUE if the record removal was initiated successfully. FALSE if the record 651 * handle is not known/have already been removed. 652 */ removeSdpRecord(int recordId)653 public boolean removeSdpRecord(int recordId){ 654 if(sNativeAvailable == false) { 655 throw new RuntimeException(TAG + " sNativeAvailable == false - native not initialized"); 656 } 657 return sdpRemoveSdpRecordNative(recordId); 658 } 659 } 660