1 /*
2  * Copyright 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.bluetooth.sdp;
17 
18 import com.android.internal.annotations.GuardedBy;
19 import com.android.internal.annotations.VisibleForTesting;
20 
21 /** Native interface to be used by SdpManager */
22 public class SdpManagerNativeInterface {
23     private static final String TAG = SdpManagerNativeInterface.class.getSimpleName();
24 
25     @GuardedBy("INSTANCE_LOCK")
26     private static SdpManagerNativeInterface sInstance;
27 
28     private static final Object INSTANCE_LOCK = new Object();
29 
30     private SdpManager mSdpManager;
31     private boolean mNativeAvailable = false;
32 
33     /** This class is a singleton because native library should only be loaded once */
getInstance()34     public static SdpManagerNativeInterface getInstance() {
35         synchronized (INSTANCE_LOCK) {
36             if (sInstance == null) {
37                 sInstance = new SdpManagerNativeInterface();
38             }
39             return sInstance;
40         }
41     }
42 
43     /** Set singleton instance. */
44     @VisibleForTesting
setInstance(SdpManagerNativeInterface instance)45     public static void setInstance(SdpManagerNativeInterface instance) {
46         synchronized (INSTANCE_LOCK) {
47             sInstance = instance;
48         }
49     }
50 
init(SdpManager sdpManager)51     void init(SdpManager sdpManager) {
52         mSdpManager = sdpManager;
53         initializeNative();
54         mNativeAvailable = true;
55     }
56 
cleanup()57     void cleanup() {
58         mNativeAvailable = false;
59         cleanupNative();
60     }
61 
isAvailable()62     public boolean isAvailable() {
63         return mNativeAvailable;
64     }
65 
sdpSearch(byte[] address, byte[] uuid)66     void sdpSearch(byte[] address, byte[] uuid) {
67         sdpSearchNative(address, uuid);
68     }
69 
70     /**
71      * Create a server side Message Access Profile Service Record. Create the record once, and reuse
72      * it for all connections. If changes to a record is needed remove the old record using {@link
73      * removeSdpRecord} and then create a new one.
74      *
75      * @param serviceName The textual name of the service
76      * @param masId The MAS ID to associate with this SDP record
77      * @param rfcommChannel The RFCOMM channel that clients can connect to (obtain from
78      *     BluetoothServerSocket)
79      * @param l2capPsm The L2CAP PSM channel that clients can connect to (obtain from
80      *     BluetoothServerSocket) Supply -1 to omit the L2CAP PSM from the record.
81      * @param version The Profile version number (As specified in the Bluetooth MAP specification)
82      * @param msgTypes The supported message types bit mask (As specified in the Bluetooth MAP
83      *     specification)
84      * @param features The feature bit mask (As specified in the Bluetooth MAP specification)
85      * @return a handle to the record created. The record can be removed again using {@link
86      *     removeSdpRecord}(). The record is not linked to the creation/destruction of
87      *     BluetoothSockets, hence SDP record cleanup is a separate process.
88      */
createMapMasRecord( String serviceName, int masId, int rfcommChannel, int l2capPsm, int version, int msgTypes, int features)89     public int createMapMasRecord(
90             String serviceName,
91             int masId,
92             int rfcommChannel,
93             int l2capPsm,
94             int version,
95             int msgTypes,
96             int features) {
97         if (!mNativeAvailable) {
98             throw new RuntimeException(TAG + " mNativeAvailable == false - native not initialized");
99         }
100         return sdpCreateMapMasRecordNative(
101                 serviceName, masId, rfcommChannel, l2capPsm, version, msgTypes, features);
102     }
103 
104     /**
105      * Create a client side Message Access Profile Service Record. Create the record once, and reuse
106      * it for all connections. If changes to a record is needed remove the old record using {@link
107      * removeSdpRecord} and then create a new one.
108      *
109      * @param serviceName The textual name of the service
110      * @param rfcommChannel The RFCOMM channel that clients can connect to (obtain from
111      *     BluetoothServerSocket)
112      * @param l2capPsm The L2CAP PSM channel that clients can connect to (obtain from
113      *     BluetoothServerSocket) Supply -1 to omit the L2CAP PSM from the record.
114      * @param version The Profile version number (As specified in the Bluetooth MAP specification)
115      * @param features The feature bit mask (As specified in the Bluetooth MAP specification)
116      * @return a handle to the record created. The record can be removed again using {@link
117      *     removeSdpRecord}(). The record is not linked to the creation/destruction of
118      *     BluetoothSockets, hence SDP record cleanup is a separate process.
119      */
createMapMnsRecord( String serviceName, int rfcommChannel, int l2capPsm, int version, int features)120     public int createMapMnsRecord(
121             String serviceName, int rfcommChannel, int l2capPsm, int version, int features) {
122         if (!mNativeAvailable) {
123             throw new RuntimeException(TAG + " mNativeAvailable == false - native not initialized");
124         }
125         return sdpCreateMapMnsRecordNative(serviceName, rfcommChannel, l2capPsm, version, features);
126     }
127 
128     /**
129      * Create a Client side Phone Book Access Profile Service Record. Create the record once, and
130      * reuse it for all connections. If changes to a record is needed remove the old record using
131      * {@link removeSdpRecord} and then create a new one.
132      *
133      * @param serviceName The textual name of the service
134      * @param version The Profile version number (As specified in the Bluetooth PBAP specification)
135      * @return a handle to the record created. The record can be removed again using {@link
136      *     removeSdpRecord}(). The record is not linked to the creation/destruction of
137      *     BluetoothSockets, hence SDP record cleanup is a separate process.
138      */
createPbapPceRecord(String serviceName, int version)139     public int createPbapPceRecord(String serviceName, int version) {
140         if (!mNativeAvailable) {
141             throw new RuntimeException(TAG + " mNativeAvailable == false - native not initialized");
142         }
143         return sdpCreatePbapPceRecordNative(serviceName, version);
144     }
145 
146     /**
147      * Create a Server side Phone Book Access Profile Service Record. Create the record once, and
148      * reuse it for all connections. If changes to a record is needed remove the old record using
149      * {@link removeSdpRecord} and then create a new one.
150      *
151      * @param serviceName The textual name of the service
152      * @param rfcommChannel The RFCOMM channel that clients can connect to (obtain from
153      *     BluetoothServerSocket)
154      * @param l2capPsm The L2CAP PSM channel that clients can connect to (obtain from
155      *     BluetoothServerSocket) Supply -1 to omit the L2CAP PSM from the record.
156      * @param version The Profile version number (As specified in the Bluetooth PBAP specification)
157      * @param repositories The supported repositories bit mask (As specified in the Bluetooth PBAP
158      *     specification)
159      * @param features The feature bit mask (As specified in the Bluetooth PBAP specification)
160      * @return a handle to the record created. The record can be removed again using {@link
161      *     removeSdpRecord}(). The record is not linked to the creation/destruction of
162      *     BluetoothSockets, hence SDP record cleanup is a separate process.
163      */
createPbapPseRecord( String serviceName, int rfcommChannel, int l2capPsm, int version, int repositories, int features)164     public int createPbapPseRecord(
165             String serviceName,
166             int rfcommChannel,
167             int l2capPsm,
168             int version,
169             int repositories,
170             int features) {
171         if (!mNativeAvailable) {
172             throw new RuntimeException(TAG + " mNativeAvailable == false - native not initialized");
173         }
174         return sdpCreatePbapPseRecordNative(
175                 serviceName, rfcommChannel, l2capPsm, version, repositories, features);
176     }
177 
178     /**
179      * Create a Server side Object Push Profile Service Record. Create the record once, and reuse it
180      * for all connections. If changes to a record is needed remove the old record using {@link
181      * removeSdpRecord} and then create a new one.
182      *
183      * @param serviceName The textual name of the service
184      * @param rfcommChannel The RFCOMM channel that clients can connect to (obtain from
185      *     BluetoothServerSocket)
186      * @param l2capPsm The L2CAP PSM channel that clients can connect to (obtain from
187      *     BluetoothServerSocket) Supply -1 to omit the L2CAP PSM from the record.
188      * @param version The Profile version number (As specified in the Bluetooth OPP specification)
189      * @param formatsList A list of the supported formats (As specified in the Bluetooth OPP
190      *     specification)
191      * @return a handle to the record created. The record can be removed again using {@link
192      *     removeSdpRecord}(). The record is not linked to the creation/destruction of
193      *     BluetoothSockets, hence SDP record cleanup is a separate process.
194      */
createOppOpsRecord( String serviceName, int rfcommChannel, int l2capPsm, int version, byte[] formatsList)195     public int createOppOpsRecord(
196             String serviceName, int rfcommChannel, int l2capPsm, int version, byte[] formatsList) {
197         if (!mNativeAvailable) {
198             throw new RuntimeException(TAG + " mNativeAvailable == false - native not initialized");
199         }
200         return sdpCreateOppOpsRecordNative(
201                 serviceName, rfcommChannel, l2capPsm, version, formatsList);
202     }
203 
204     /**
205      * Create a server side Sim Access Profile Service Record. Create the record once, and reuse it
206      * for all connections. If changes to a record is needed remove the old record using {@link
207      * removeSdpRecord} and then create a new one.
208      *
209      * @param serviceName The textual name of the service
210      * @param rfcommChannel The RFCOMM channel that clients can connect to (obtain from
211      *     BluetoothServerSocket)
212      * @param version The Profile version number (As specified in the Bluetooth SAP specification)
213      * @return a handle to the record created. The record can be removed again using {@link
214      *     removeSdpRecord}(). The record is not linked to the creation/destruction of
215      *     BluetoothSockets, hence SDP record cleanup is a separate process.
216      */
createSapsRecord(String serviceName, int rfcommChannel, int version)217     public int createSapsRecord(String serviceName, int rfcommChannel, int version) {
218         if (!mNativeAvailable) {
219             throw new RuntimeException(TAG + " mNativeAvailable == false - native not initialized");
220         }
221         return sdpCreateSapsRecordNative(serviceName, rfcommChannel, version);
222     }
223 
224     /**
225      * Remove a SDP record. When Bluetooth is disabled all records will be deleted, hence there is
226      * no need to call this function when bluetooth is disabled.
227      *
228      * @param recordId The Id returned by on of the createXxxXxxRecord() functions.
229      * @return TRUE if the record removal was initiated successfully. FALSE if the record handle is
230      *     not known/have already been removed.
231      */
removeSdpRecord(int recordId)232     public boolean removeSdpRecord(int recordId) {
233         if (!mNativeAvailable) {
234             throw new RuntimeException(TAG + " mNativeAvailable == false - native not initialized");
235         }
236         return sdpRemoveSdpRecordNative(recordId);
237     }
238 
239     /**********************************************************************************************/
240     /*********************************** callbacks from native ************************************/
241     /**********************************************************************************************/
242 
sdpRecordFoundCallback( int status, byte[] address, byte[] uuid, int sizeRecord, byte[] record)243     void sdpRecordFoundCallback(
244             int status, byte[] address, byte[] uuid, int sizeRecord, byte[] record) {
245         mSdpManager.sdpRecordFoundCallback(status, address, uuid, sizeRecord, record);
246     }
247 
sdpMasRecordFoundCallback( int status, byte[] address, byte[] uuid, int masInstanceId, int l2capPsm, int rfcommCannelNumber, int profileVersion, int supportedFeatures, int supportedMessageTypes, String serviceName, boolean moreResults)248     void sdpMasRecordFoundCallback(
249             int status,
250             byte[] address,
251             byte[] uuid,
252             int masInstanceId,
253             int l2capPsm,
254             int rfcommCannelNumber,
255             int profileVersion,
256             int supportedFeatures,
257             int supportedMessageTypes,
258             String serviceName,
259             boolean moreResults) {
260         mSdpManager.sdpMasRecordFoundCallback(
261                 status,
262                 address,
263                 uuid,
264                 masInstanceId,
265                 l2capPsm,
266                 rfcommCannelNumber,
267                 profileVersion,
268                 supportedFeatures,
269                 supportedMessageTypes,
270                 serviceName,
271                 moreResults);
272     }
273 
sdpMnsRecordFoundCallback( int status, byte[] address, byte[] uuid, int l2capPsm, int rfcommCannelNumber, int profileVersion, int supportedFeatures, String serviceName, boolean moreResults)274     void sdpMnsRecordFoundCallback(
275             int status,
276             byte[] address,
277             byte[] uuid,
278             int l2capPsm,
279             int rfcommCannelNumber,
280             int profileVersion,
281             int supportedFeatures,
282             String serviceName,
283             boolean moreResults) {
284         mSdpManager.sdpMnsRecordFoundCallback(
285                 status,
286                 address,
287                 uuid,
288                 l2capPsm,
289                 rfcommCannelNumber,
290                 profileVersion,
291                 supportedFeatures,
292                 serviceName,
293                 moreResults);
294     }
295 
sdpPseRecordFoundCallback( int status, byte[] address, byte[] uuid, int l2capPsm, int rfcommCannelNumber, int profileVersion, int supportedFeatures, int supportedRepositories, String serviceName, boolean moreResults)296     void sdpPseRecordFoundCallback(
297             int status,
298             byte[] address,
299             byte[] uuid,
300             int l2capPsm,
301             int rfcommCannelNumber,
302             int profileVersion,
303             int supportedFeatures,
304             int supportedRepositories,
305             String serviceName,
306             boolean moreResults) {
307         mSdpManager.sdpPseRecordFoundCallback(
308                 status,
309                 address,
310                 uuid,
311                 l2capPsm,
312                 rfcommCannelNumber,
313                 profileVersion,
314                 supportedFeatures,
315                 supportedRepositories,
316                 serviceName,
317                 moreResults);
318     }
319 
sdpOppOpsRecordFoundCallback( int status, byte[] address, byte[] uuid, int l2capPsm, int rfcommCannelNumber, int profileVersion, String serviceName, byte[] formatsList, boolean moreResults)320     void sdpOppOpsRecordFoundCallback(
321             int status,
322             byte[] address,
323             byte[] uuid,
324             int l2capPsm,
325             int rfcommCannelNumber,
326             int profileVersion,
327             String serviceName,
328             byte[] formatsList,
329             boolean moreResults) {
330         mSdpManager.sdpOppOpsRecordFoundCallback(
331                 status,
332                 address,
333                 uuid,
334                 l2capPsm,
335                 rfcommCannelNumber,
336                 profileVersion,
337                 serviceName,
338                 formatsList,
339                 moreResults);
340     }
341 
sdpSapsRecordFoundCallback( int status, byte[] address, byte[] uuid, int rfcommCannelNumber, int profileVersion, String serviceName, boolean moreResults)342     void sdpSapsRecordFoundCallback(
343             int status,
344             byte[] address,
345             byte[] uuid,
346             int rfcommCannelNumber,
347             int profileVersion,
348             String serviceName,
349             boolean moreResults) {
350         mSdpManager.sdpSapsRecordFoundCallback(
351                 status,
352                 address,
353                 uuid,
354                 rfcommCannelNumber,
355                 profileVersion,
356                 serviceName,
357                 moreResults);
358     }
359 
sdpDipRecordFoundCallback( int status, byte[] address, byte[] uuid, int specificationId, int vendorId, int vendorIdSource, int productId, int version, boolean primaryRecord, boolean moreResults)360     void sdpDipRecordFoundCallback(
361             int status,
362             byte[] address,
363             byte[] uuid,
364             int specificationId,
365             int vendorId,
366             int vendorIdSource,
367             int productId,
368             int version,
369             boolean primaryRecord,
370             boolean moreResults) {
371         mSdpManager.sdpDipRecordFoundCallback(
372                 status,
373                 address,
374                 uuid,
375                 specificationId,
376                 vendorId,
377                 vendorIdSource,
378                 productId,
379                 version,
380                 primaryRecord,
381                 moreResults);
382     }
383 
384     /**********************************************************************************************/
385     /******************************************* native *******************************************/
386     /**********************************************************************************************/
387 
initializeNative()388     private native void initializeNative();
389 
cleanupNative()390     private native void cleanupNative();
391 
sdpSearchNative(byte[] address, byte[] uuid)392     private native boolean sdpSearchNative(byte[] address, byte[] uuid);
393 
sdpCreateMapMasRecordNative( String serviceName, int masId, int rfcommChannel, int l2capPsm, int version, int msgTypes, int features)394     private native int sdpCreateMapMasRecordNative(
395             String serviceName,
396             int masId,
397             int rfcommChannel,
398             int l2capPsm,
399             int version,
400             int msgTypes,
401             int features);
402 
sdpCreateMapMnsRecordNative( String serviceName, int rfcommChannel, int l2capPsm, int version, int features)403     private native int sdpCreateMapMnsRecordNative(
404             String serviceName, int rfcommChannel, int l2capPsm, int version, int features);
405 
sdpCreatePbapPceRecordNative(String serviceName, int version)406     private native int sdpCreatePbapPceRecordNative(String serviceName, int version);
407 
sdpCreatePbapPseRecordNative( String serviceName, int rfcommChannel, int l2capPsm, int version, int repositories, int features)408     private native int sdpCreatePbapPseRecordNative(
409             String serviceName,
410             int rfcommChannel,
411             int l2capPsm,
412             int version,
413             int repositories,
414             int features);
415 
sdpCreateOppOpsRecordNative( String serviceName, int rfcommChannel, int l2capPsm, int version, byte[] formatsList)416     private native int sdpCreateOppOpsRecordNative(
417             String serviceName, int rfcommChannel, int l2capPsm, int version, byte[] formatsList);
418 
sdpCreateSapsRecordNative( String serviceName, int rfcommChannel, int version)419     private native int sdpCreateSapsRecordNative(
420             String serviceName, int rfcommChannel, int version);
421 
sdpRemoveSdpRecordNative(int recordId)422     private native boolean sdpRemoveSdpRecordNative(int recordId);
423 }
424