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