• 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 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