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