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