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