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