• 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                 if (inst.getDevice().getAddress().equals(addressString) && inst.getUuid()
190                         .equals(uuid)) {
191                     return inst;
192                 }
193             }
194             return null;
195         }
196 
isSearching(BluetoothDevice device, ParcelUuid uuid)197         boolean isSearching(BluetoothDevice device, ParcelUuid uuid) {
198             String addressString = device.getAddress();
199             for (SdpSearchInstance inst : mList) {
200                 if (inst.getDevice().getAddress().equals(addressString) && inst.getUuid()
201                         .equals(uuid)) {
202                     return inst.isSearching();
203                 }
204             }
205             return false;
206         }
207     }
208 
209 
SdpManager(AdapterService adapterService)210     private SdpManager(AdapterService adapterService) {
211         sSdpSearchTracker = new SdpSearchTracker();
212         sAdapterService = adapterService;
213         initializeNative();
214         sNativeAvailable = true;
215     }
216 
217 
init(AdapterService adapterService)218     public static SdpManager init(AdapterService adapterService) {
219         sSdpManager = new SdpManager(adapterService);
220         return sSdpManager;
221     }
222 
getDefaultManager()223     public static SdpManager getDefaultManager() {
224         return sSdpManager;
225     }
226 
cleanup()227     public void cleanup() {
228         if (sSdpSearchTracker != null) {
229             synchronized (TRACKER_LOCK) {
230                 sSdpSearchTracker.clear();
231             }
232         }
233 
234         if (sNativeAvailable) {
235             cleanupNative();
236             sNativeAvailable = false;
237         }
238         sSdpManager = null;
239     }
240 
241 
sdpMasRecordFoundCallback(int status, byte[] address, byte[] uuid, int masInstanceId, int l2capPsm, int rfcommCannelNumber, int profileVersion, int supportedFeatures, int supportedMessageTypes, String serviceName, boolean moreResults)242     void sdpMasRecordFoundCallback(int status, byte[] address, byte[] uuid, int masInstanceId,
243             int l2capPsm, int rfcommCannelNumber, int profileVersion, int supportedFeatures,
244             int supportedMessageTypes, String serviceName, boolean moreResults) {
245 
246         synchronized (TRACKER_LOCK) {
247             SdpSearchInstance inst = sSdpSearchTracker.getSearchInstance(address, uuid);
248             SdpMasRecord sdpRecord = null;
249             if (inst == null) {
250                 Log.e(TAG, "sdpRecordFoundCallback: Search instance is NULL");
251                 return;
252             }
253             inst.setStatus(status);
254             if (status == AbstractionLayer.BT_STATUS_SUCCESS) {
255                 sdpRecord = new SdpMasRecord(masInstanceId, l2capPsm, rfcommCannelNumber,
256                         profileVersion, supportedFeatures, supportedMessageTypes, serviceName);
257             }
258             if (D) {
259                 Log.d(TAG, "UUID: " + Arrays.toString(uuid));
260             }
261             if (D) {
262                 Log.d(TAG, "UUID in parcel: " + ((Utils.byteArrayToUuid(uuid))[0]).toString());
263             }
264             sendSdpIntent(inst, sdpRecord, moreResults);
265         }
266     }
267 
sdpMnsRecordFoundCallback(int status, byte[] address, byte[] uuid, int l2capPsm, int rfcommCannelNumber, int profileVersion, int supportedFeatures, String serviceName, boolean moreResults)268     void sdpMnsRecordFoundCallback(int status, byte[] address, byte[] uuid, int l2capPsm,
269             int rfcommCannelNumber, int profileVersion, int supportedFeatures, String serviceName,
270             boolean moreResults) {
271         synchronized (TRACKER_LOCK) {
272 
273             SdpSearchInstance inst = sSdpSearchTracker.getSearchInstance(address, uuid);
274             SdpMnsRecord sdpRecord = null;
275             if (inst == null) {
276                 Log.e(TAG, "sdpRecordFoundCallback: Search instance is NULL");
277                 return;
278             }
279             inst.setStatus(status);
280             if (status == AbstractionLayer.BT_STATUS_SUCCESS) {
281                 sdpRecord = new SdpMnsRecord(l2capPsm, rfcommCannelNumber, profileVersion,
282                         supportedFeatures, serviceName);
283             }
284             if (D) {
285                 Log.d(TAG, "UUID: " + Arrays.toString(uuid));
286             }
287             if (D) {
288                 Log.d(TAG, "UUID in parcel: " + ((Utils.byteArrayToUuid(uuid))[0]).toString());
289             }
290             sendSdpIntent(inst, sdpRecord, moreResults);
291         }
292     }
293 
sdpPseRecordFoundCallback(int status, byte[] address, byte[] uuid, int l2capPsm, int rfcommCannelNumber, int profileVersion, int supportedFeatures, int supportedRepositories, String serviceName, boolean moreResults)294     void sdpPseRecordFoundCallback(int status, byte[] address, byte[] uuid, int l2capPsm,
295             int rfcommCannelNumber, int profileVersion, int supportedFeatures,
296             int supportedRepositories, String serviceName, boolean moreResults) {
297         synchronized (TRACKER_LOCK) {
298             SdpSearchInstance inst = sSdpSearchTracker.getSearchInstance(address, uuid);
299             SdpPseRecord sdpRecord = null;
300             if (inst == null) {
301                 Log.e(TAG, "sdpRecordFoundCallback: Search instance is NULL");
302                 return;
303             }
304             inst.setStatus(status);
305             if (status == AbstractionLayer.BT_STATUS_SUCCESS) {
306                 sdpRecord = new SdpPseRecord(l2capPsm, rfcommCannelNumber, profileVersion,
307                         supportedFeatures, supportedRepositories, serviceName);
308             }
309             if (D) {
310                 Log.d(TAG, "UUID: " + Arrays.toString(uuid));
311             }
312             if (D) {
313                 Log.d(TAG, "UUID in parcel: " + ((Utils.byteArrayToUuid(uuid))[0]).toString());
314             }
315             sendSdpIntent(inst, sdpRecord, moreResults);
316         }
317     }
318 
sdpOppOpsRecordFoundCallback(int status, byte[] address, byte[] uuid, int l2capPsm, int rfcommCannelNumber, int profileVersion, String serviceName, byte[] formatsList, boolean moreResults)319     void sdpOppOpsRecordFoundCallback(int status, byte[] address, byte[] uuid, int l2capPsm,
320             int rfcommCannelNumber, int profileVersion, String serviceName, byte[] formatsList,
321             boolean moreResults) {
322 
323         synchronized (TRACKER_LOCK) {
324             SdpSearchInstance inst = sSdpSearchTracker.getSearchInstance(address, uuid);
325             SdpOppOpsRecord sdpRecord = null;
326 
327             if (inst == null) {
328                 Log.e(TAG, "sdpOppOpsRecordFoundCallback: Search instance is NULL");
329                 return;
330             }
331             inst.setStatus(status);
332             if (status == AbstractionLayer.BT_STATUS_SUCCESS) {
333                 sdpRecord = new SdpOppOpsRecord(serviceName, rfcommCannelNumber, l2capPsm,
334                         profileVersion, formatsList);
335             }
336             if (D) {
337                 Log.d(TAG, "UUID: " + Arrays.toString(uuid));
338             }
339             if (D) {
340                 Log.d(TAG, "UUID in parcel: " + ((Utils.byteArrayToUuid(uuid))[0]).toString());
341             }
342             sendSdpIntent(inst, sdpRecord, moreResults);
343         }
344     }
345 
sdpSapsRecordFoundCallback(int status, byte[] address, byte[] uuid, int rfcommCannelNumber, int profileVersion, String serviceName, boolean moreResults)346     void sdpSapsRecordFoundCallback(int status, byte[] address, byte[] uuid, int rfcommCannelNumber,
347             int profileVersion, String serviceName, boolean moreResults) {
348 
349         synchronized (TRACKER_LOCK) {
350             SdpSearchInstance inst = sSdpSearchTracker.getSearchInstance(address, uuid);
351             SdpSapsRecord sdpRecord = null;
352             if (inst == null) {
353                 Log.e(TAG, "sdpSapsRecordFoundCallback: Search instance is NULL");
354                 return;
355             }
356             inst.setStatus(status);
357             if (status == AbstractionLayer.BT_STATUS_SUCCESS) {
358                 sdpRecord = new SdpSapsRecord(rfcommCannelNumber, profileVersion, serviceName);
359             }
360             if (D) {
361                 Log.d(TAG, "UUID: " + Arrays.toString(uuid));
362             }
363             if (D) {
364                 Log.d(TAG, "UUID in parcel: " + ((Utils.byteArrayToUuid(uuid))[0]).toString());
365             }
366             sendSdpIntent(inst, sdpRecord, moreResults);
367         }
368     }
369 
sdpDipRecordFoundCallback(int status, byte[] address, byte[] uuid, int specificationId, int vendorId, int vendorIdSource, int productId, int version, boolean primaryRecord, boolean moreResults)370     void sdpDipRecordFoundCallback(int status, byte[] address,
371             byte[] uuid,  int specificationId,
372             int vendorId, int vendorIdSource,
373             int productId, int version,
374             boolean primaryRecord,
375             boolean moreResults) {
376         synchronized(TRACKER_LOCK) {
377             SdpSearchInstance inst = sSdpSearchTracker.getSearchInstance(address, uuid);
378             SdpDipRecord sdpRecord = null;
379             if (inst == null) {
380               Log.e(TAG, "sdpDipRecordFoundCallback: Search instance is NULL");
381               return;
382             }
383             inst.setStatus(status);
384             if (D) {
385                 Log.d(TAG, "sdpDipRecordFoundCallback: status " + status);
386             }
387             if (status == AbstractionLayer.BT_STATUS_SUCCESS) {
388                 sdpRecord = new SdpDipRecord(specificationId,
389                         vendorId, vendorIdSource,
390                         productId, version,
391                         primaryRecord);
392             }
393             if (D) {
394                 Log.d(TAG, "UUID: " + Arrays.toString(uuid));
395             }
396             if (D) {
397                 Log.d(TAG, "UUID in parcel: " + ((Utils.byteArrayToUuid(uuid))[0]).toString());
398             }
399             sendSdpIntent(inst, sdpRecord, moreResults);
400         }
401     }
402 
403     /* TODO: Test or remove! */
sdpRecordFoundCallback(int status, byte[] address, byte[] uuid, int sizeRecord, byte[] record)404     void sdpRecordFoundCallback(int status, byte[] address, byte[] uuid, int sizeRecord,
405             byte[] record) {
406         synchronized (TRACKER_LOCK) {
407 
408             SdpSearchInstance inst = sSdpSearchTracker.getSearchInstance(address, uuid);
409             SdpRecord sdpRecord = null;
410             if (inst == null) {
411                 Log.e(TAG, "sdpRecordFoundCallback: Search instance is NULL");
412                 return;
413             }
414             inst.setStatus(status);
415             if (status == AbstractionLayer.BT_STATUS_SUCCESS) {
416                 if (D) {
417                     Log.d(TAG, "sdpRecordFoundCallback: found a sdp record of size " + sizeRecord);
418                 }
419                 if (D) {
420                     Log.d(TAG, "Record:" + Arrays.toString(record));
421                 }
422                 sdpRecord = new SdpRecord(sizeRecord, record);
423             }
424             if (D) {
425                 Log.d(TAG, "UUID: " + Arrays.toString(uuid));
426             }
427             if (D) {
428                 Log.d(TAG, "UUID in parcel: " + ((Utils.byteArrayToUuid(uuid))[0]).toString());
429             }
430             sendSdpIntent(inst, sdpRecord, false);
431         }
432     }
433 
sdpSearch(BluetoothDevice device, ParcelUuid uuid)434     public void sdpSearch(BluetoothDevice device, ParcelUuid uuid) {
435         if (!sNativeAvailable) {
436             Log.e(TAG, "Native not initialized!");
437             return;
438         }
439         synchronized (TRACKER_LOCK) {
440             if (sSdpSearchTracker.isSearching(device, uuid)) {
441                 /* Search already in progress */
442                 return;
443             }
444 
445             SdpSearchInstance inst = new SdpSearchInstance(0, device, uuid);
446             sSdpSearchTracker.add(inst); // Queue the request
447 
448             startSearch(); // Start search if not busy
449         }
450 
451     }
452 
453     /* Caller must hold the mTrackerLock */
startSearch()454     private void startSearch() {
455 
456         SdpSearchInstance inst = sSdpSearchTracker.getNext();
457 
458         if ((inst != null) && (!sSearchInProgress)) {
459             if (D) {
460                 Log.d(TAG, "Starting search for UUID: " + inst.getUuid());
461             }
462             sSearchInProgress = true;
463 
464             inst.startSearch(); // Trigger timeout message
465 
466             sdpSearchNative(sAdapterService.getByteIdentityAddress(inst.getDevice()),
467                     Utils.uuidToByteArray(inst.getUuid()));
468         } else { // Else queue is empty.
469             if (D) {
470                 Log.d(TAG, "startSearch(): nextInst = " + inst + " mSearchInProgress = "
471                         + sSearchInProgress + " - search busy or queue empty.");
472             }
473         }
474     }
475 
476     /* Caller must hold the mTrackerLock */
sendSdpIntent(SdpSearchInstance inst, Parcelable record, boolean moreResults)477     private void sendSdpIntent(SdpSearchInstance inst, Parcelable record, boolean moreResults) {
478 
479         inst.stopSearch();
480 
481         Intent intent = new Intent(BluetoothDevice.ACTION_SDP_RECORD);
482 
483         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, inst.getDevice());
484         intent.putExtra(BluetoothDevice.EXTRA_SDP_SEARCH_STATUS, inst.getStatus());
485         if (record != null) {
486             intent.putExtra(BluetoothDevice.EXTRA_SDP_RECORD, record);
487         }
488         intent.putExtra(BluetoothDevice.EXTRA_UUID, inst.getUuid());
489         /* TODO:  BLUETOOTH_ADMIN_PERM was private... change to callback interface.
490          * Keep in mind that the MAP client needs to use this as well,
491          * hence to make it call-backs, the MAP client profile needs to be
492          * part of the Bluetooth APK. */
493         sAdapterService.sendBroadcast(intent, BLUETOOTH_CONNECT,
494                 Utils.getTempAllowlistBroadcastOptions());
495 
496         if (!moreResults) {
497             //Remove the outstanding UUID request
498             sSdpSearchTracker.remove(inst);
499             sSearchInProgress = false;
500             startSearch();
501         }
502     }
503 
504     private final Handler mHandler = new Handler() {
505         @Override
506         public void handleMessage(Message msg) {
507             switch (msg.what) {
508                 case MESSAGE_SDP_INTENT:
509                     SdpSearchInstance msgObj = (SdpSearchInstance) msg.obj;
510                     Log.w(TAG, "Search timedout for UUID " + msgObj.getUuid());
511                     synchronized (TRACKER_LOCK) {
512                         sendSdpIntent(msgObj, null, false);
513                     }
514                     break;
515             }
516         }
517     };
518 
519     /**
520      * Create a server side Message Access Profile Service Record.
521      * Create the record once, and reuse it for all connections.
522      * If changes to a record is needed remove the old record using {@link removeSdpRecord}
523      * and then create a new one.
524      * @param serviceName   The textual name of the service
525      * @param masId         The MAS ID to associate with this SDP record
526      * @param rfcommChannel The RFCOMM channel that clients can connect to
527      *                      (obtain from BluetoothServerSocket)
528      * @param l2capPsm      The L2CAP PSM channel that clients can connect to
529      *                      (obtain from BluetoothServerSocket)
530      *                      Supply -1 to omit the L2CAP PSM from the record.
531      * @param version       The Profile version number (As specified in the Bluetooth
532      *                      MAP specification)
533      * @param msgTypes      The supported message types bit mask (As specified in
534      *                      the Bluetooth MAP specification)
535      * @param features      The feature bit mask (As specified in the Bluetooth
536      *                       MAP specification)
537      * @return a handle to the record created. The record can be removed again
538      *          using {@link removeSdpRecord}(). The record is not linked to the
539      *          creation/destruction of BluetoothSockets, hence SDP record cleanup
540      *          is a separate process.
541      */
createMapMasRecord(String serviceName, int masId, int rfcommChannel, int l2capPsm, int version, int msgTypes, int features)542     public int createMapMasRecord(String serviceName, int masId, int rfcommChannel, int l2capPsm,
543             int version, int msgTypes, int features) {
544         if (!sNativeAvailable) {
545             throw new RuntimeException(TAG + " sNativeAvailable == false - native not initialized");
546         }
547         return sdpCreateMapMasRecordNative(serviceName, masId, rfcommChannel, l2capPsm, version,
548                 msgTypes, features);
549     }
550 
551     /**
552      * Create a client side Message Access Profile Service Record.
553      * Create the record once, and reuse it for all connections.
554      * If changes to a record is needed remove the old record using {@link removeSdpRecord}
555      * and then create a new one.
556      * @param serviceName   The textual name of the service
557      * @param rfcommChannel The RFCOMM channel that clients can connect to
558      *                      (obtain from BluetoothServerSocket)
559      * @param l2capPsm      The L2CAP PSM channel that clients can connect to
560      *                      (obtain from BluetoothServerSocket)
561      *                      Supply -1 to omit the L2CAP PSM from the record.
562      * @param version       The Profile version number (As specified in the Bluetooth
563      *                      MAP specification)
564      * @param features      The feature bit mask (As specified in the Bluetooth
565      *                       MAP specification)
566      * @return a handle to the record created. The record can be removed again
567      *          using {@link removeSdpRecord}(). The record is not linked to the
568      *          creation/destruction of BluetoothSockets, hence SDP record cleanup
569      *          is a separate process.
570      */
createMapMnsRecord(String serviceName, int rfcommChannel, int l2capPsm, int version, int features)571     public int createMapMnsRecord(String serviceName, int rfcommChannel, int l2capPsm, int version,
572             int features) {
573         if (!sNativeAvailable) {
574             throw new RuntimeException(TAG + " sNativeAvailable == false - native not initialized");
575         }
576         return sdpCreateMapMnsRecordNative(serviceName, rfcommChannel, l2capPsm, version, features);
577     }
578 
579      /**
580      * Create a Client side Phone Book Access Profile Service Record.
581      * Create the record once, and reuse it for all connections.
582      * If changes to a record is needed remove the old record using {@link removeSdpRecord}
583      * and then create a new one.
584      * @param serviceName   The textual name of the service
585      * @param version       The Profile version number (As specified in the Bluetooth
586      *                      PBAP specification)
587      * @return a handle to the record created. The record can be removed again
588      *          using {@link removeSdpRecord}(). The record is not linked to the
589      *          creation/destruction of BluetoothSockets, hence SDP record cleanup
590      *          is a separate process.
591      */
createPbapPceRecord(String serviceName, int version)592     public int createPbapPceRecord(String serviceName, int version) {
593         if (!sNativeAvailable) {
594             throw new RuntimeException(TAG + " sNativeAvailable == false - native not initialized");
595         }
596         return sdpCreatePbapPceRecordNative(serviceName, version);
597     }
598 
599 
600     /**
601      * Create a Server side Phone Book 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 l2capPsm      The L2CAP PSM channel that clients can connect to
609      *                      (obtain from BluetoothServerSocket)
610      *                      Supply -1 to omit the L2CAP PSM from the record.
611      * @param version       The Profile version number (As specified in the Bluetooth
612      *                      PBAP specification)
613      * @param repositories  The supported repositories bit mask (As specified in
614      *                      the Bluetooth PBAP specification)
615      * @param features      The feature bit mask (As specified in the Bluetooth
616      *                      PBAP specification)
617      * @return a handle to the record created. The record can be removed again
618      *          using {@link removeSdpRecord}(). The record is not linked to the
619      *          creation/destruction of BluetoothSockets, hence SDP record cleanup
620      *          is a separate process.
621      */
createPbapPseRecord(String serviceName, int rfcommChannel, int l2capPsm, int version, int repositories, int features)622     public int createPbapPseRecord(String serviceName, int rfcommChannel, int l2capPsm, int version,
623             int repositories, int features) {
624         if (!sNativeAvailable) {
625             throw new RuntimeException(TAG + " sNativeAvailable == false - native not initialized");
626         }
627         return sdpCreatePbapPseRecordNative(serviceName, rfcommChannel, l2capPsm, version,
628                 repositories, features);
629     }
630 
631     /**
632      * Create a Server side Object Push Profile Service Record.
633      * Create the record once, and reuse it for all connections.
634      * If changes to a record is needed remove the old record using {@link removeSdpRecord}
635      * and then create a new one.
636      * @param serviceName   The textual name of the service
637      * @param rfcommChannel The RFCOMM channel that clients can connect to
638      *                      (obtain from BluetoothServerSocket)
639      * @param l2capPsm      The L2CAP PSM channel that clients can connect to
640      *                      (obtain from BluetoothServerSocket)
641      *                      Supply -1 to omit the L2CAP PSM from the record.
642      * @param version       The Profile version number (As specified in the Bluetooth
643      *                      OPP specification)
644      * @param formatsList  A list of the supported formats (As specified in
645      *                      the Bluetooth OPP specification)
646      * @return a handle to the record created. The record can be removed again
647      *          using {@link removeSdpRecord}(). The record is not linked to the
648      *          creation/destruction of BluetoothSockets, hence SDP record cleanup
649      *          is a separate process.
650      */
createOppOpsRecord(String serviceName, int rfcommChannel, int l2capPsm, int version, byte[] formatsList)651     public int createOppOpsRecord(String serviceName, int rfcommChannel, int l2capPsm, int version,
652             byte[] formatsList) {
653         if (!sNativeAvailable) {
654             throw new RuntimeException(TAG + " sNativeAvailable == false - native not initialized");
655         }
656         return sdpCreateOppOpsRecordNative(serviceName, rfcommChannel, l2capPsm, version,
657                 formatsList);
658     }
659 
660     /**
661      * Create a server side Sim Access Profile Service Record.
662      * Create the record once, and reuse it for all connections.
663      * If changes to a record is needed remove the old record using {@link removeSdpRecord}
664      * and then create a new one.
665      * @param serviceName   The textual name of the service
666      * @param rfcommChannel The RFCOMM channel that clients can connect to
667      *                      (obtain from BluetoothServerSocket)
668      * @param version       The Profile version number (As specified in the Bluetooth
669      *                      SAP specification)
670      * @return a handle to the record created. The record can be removed again
671      *          using {@link removeSdpRecord}(). The record is not linked to the
672      *          creation/destruction of BluetoothSockets, hence SDP record cleanup
673      *          is a separate process.
674      */
createSapsRecord(String serviceName, int rfcommChannel, int version)675     public int createSapsRecord(String serviceName, int rfcommChannel, int version) {
676         if (!sNativeAvailable) {
677             throw new RuntimeException(TAG + " sNativeAvailable == false - native not initialized");
678         }
679         return sdpCreateSapsRecordNative(serviceName, rfcommChannel, version);
680     }
681 
682     /**
683      * Remove a SDP record.
684      * When Bluetooth is disabled all records will be deleted, hence there
685      * is no need to call this function when bluetooth is disabled.
686      * @param recordId The Id returned by on of the createXxxXxxRecord() functions.
687      * @return TRUE if the record removal was initiated successfully. FALSE if the record
688      *         handle is not known/have already been removed.
689      */
removeSdpRecord(int recordId)690     public boolean removeSdpRecord(int recordId) {
691         if (!sNativeAvailable) {
692             throw new RuntimeException(TAG + " sNativeAvailable == false - native not initialized");
693         }
694         return sdpRemoveSdpRecordNative(recordId);
695     }
696 }
697