Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f081f8a3 authored by Casper Bonde's avatar Casper Bonde Committed by Android Git Automerger
Browse files

am 448d426c: SAP: Change to use new SDP Api (2/4)

* commit '448d426c':
  SAP: Change to use new SDP Api (2/4)
parents f5e76d3b 448d426c
Loading
Loading
Loading
Loading
+55 −2
Original line number Diff line number Diff line
@@ -35,7 +35,8 @@ static const uint8_t UUID_MAP_MNS[] = {0x00, 0x00, 0x11, 0x33, 0x00, 0x00, 0x10
                                        0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB};
static const uint8_t  UUID_SPP[] = {0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x10, 0x00,
                                    0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB};

static const uint8_t  UUID_SAP[] = {0x00, 0x00, 0x11, 0x2D, 0x00, 0x00, 0x10, 0x00,
                                    0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB};
// TODO:
// Both the fact that the UUIDs are declared in multiple places, plus the fact
// that there is a mess of UUID comparison and shortening methods will have to
@@ -52,6 +53,7 @@ static jmethodID method_sdpMasRecordFoundCallback;
static jmethodID method_sdpMnsRecordFoundCallback;
static jmethodID method_sdpPseRecordFoundCallback;
static jmethodID method_sdpOppOpsRecordFoundCallback;
static jmethodID method_sdpSapsRecordFoundCallback;

static const btsdp_interface_t *sBluetoothSdpInterface = NULL;

@@ -123,6 +125,10 @@ static void classInitNative(JNIEnv* env, jclass clazz) {
    method_sdpOppOpsRecordFoundCallback = env->GetMethodID(clazz,
                                                    "sdpOppOpsRecordFoundCallback",
                                                    "(I[B[BIIILjava/lang/String;[BZ)V");
    /* SAP Server record */
    method_sdpSapsRecordFoundCallback = env->GetMethodID(clazz,
                                                    "sdpSapsRecordFoundCallback",
                                                    "(I[B[BIILjava/lang/String;Z)V");

}

@@ -261,6 +267,15 @@ static void sdp_search_callback(bt_status_t status, bt_bdaddr_t *bd_addr, uint8_
                    more_results);
            sCallbackEnv->DeleteLocalRef(formats_list);

        } else if (IS_UUID(UUID_SAP, uuid_in)) {
            sCallbackEnv->CallVoidMethod(sCallbacksObj, method_sdpSapsRecordFoundCallback,
                    (jint) status,
                    addr,
                    uuid,
                    (jint)record->mas.hdr.rfcomm_channel_number,
                    (jint)record->mas.hdr.profile_version,
                    service_name,
                    more_results);
        } else {
            // we don't have a wrapper for this uuid, send as raw data
            jint record_data_size = record->hdr.user1_ptr_len;
@@ -467,6 +482,42 @@ static jint sdpCreateOppOpsRecordNative(JNIEnv *env, jobject obj, jstring name_s
    return handle;
}

static jint sdpCreateSapsRecordNative(JNIEnv *env, jobject obj, jstring name_str,
                                         jint scn, jint version) {
    ALOGD("%s:",__FUNCTION__);

    const char* service_name = NULL;
    bluetooth_sdp_record record = {}; // Must be zero initialized
    int handle = -1;
    int ret = 0;
    if (!sBluetoothSdpInterface) return handle;

    record.sap.hdr.type = SDP_TYPE_SAP_SERVER;

    if (name_str != NULL) {
        service_name = env->GetStringUTFChars(name_str, NULL);
        record.mas.hdr.service_name = (char *) service_name;
        record.mas.hdr.service_name_length = strlen(service_name);
    } else {
        record.mas.hdr.service_name = NULL;
        record.mas.hdr.service_name_length = 0;
    }
    record.mas.hdr.rfcomm_channel_number = scn;
    record.mas.hdr.profile_version = version;

    if ( (ret = sBluetoothSdpInterface->create_sdp_record(&record, &handle))
            != BT_STATUS_SUCCESS) {
        ALOGE("SDP Create record failed: %d", ret);
        goto Fail;
    }

    ALOGD("SDP Create record success - handle: %d", handle);

    Fail:
    if (service_name) env->ReleaseStringUTFChars(name_str, service_name);
    return handle;
}

static jboolean sdpRemoveSdpRecordNative(JNIEnv *env, jobject obj, jint record_id) {
    ALOGD("%s:",__FUNCTION__);

@@ -520,6 +571,8 @@ static JNINativeMethod sMethods[] = {
        (void*) sdpCreatePbapPseRecordNative},
    {"sdpCreateOppOpsRecordNative", "(Ljava/lang/String;III[B)I",
        (void*) sdpCreateOppOpsRecordNative},
    {"sdpCreateSapsRecordNative", "(Ljava/lang/String;II)I",
        (void*) sdpCreateSapsRecordNative},
    {"sdpRemoveSdpRecordNative", "(I)Z", (void*) sdpRemoveSdpRecordNative}
};

+18 −2
Original line number Diff line number Diff line
@@ -37,10 +37,13 @@ import com.android.bluetooth.Utils;
import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.btservice.ProfileService;
import com.android.bluetooth.btservice.ProfileService.IProfileServiceBinder;
import com.android.bluetooth.sdp.SdpManager;

@TargetApi(Build.VERSION_CODES.ECLAIR)
public class SapService extends ProfileService {

    private static final String SDP_SAP_SERVICE_NAME = "SIM Access";
    private static final int SDP_SAP_VERSION = 0x0102;
    private static final String TAG = "SapService";
    public static final boolean DEBUG = true;
    public static final boolean VERBOSE = true;
@@ -64,6 +67,7 @@ public class SapService extends ProfileService {
    private BluetoothAdapter mAdapter;
    private SocketAcceptThread mAcceptThread = null;
    private BluetoothServerSocket mServerSocket = null;
    private int mSdpHandle = -1;
    private BluetoothSocket mConnSocket = null;
    private BluetoothDevice mRemoteDevice = null;
    private static String sRemoteDeviceName = null;
@@ -109,10 +113,22 @@ public class SapService extends ProfileService {
        for (int i = 0; i < CREATE_RETRY_TIME && !mInterrupted; i++) {
            initSocketOK = true;
            try {
                //
                // It is mandatory for MSE to support initiation of bonding and
                // encryption.
                mServerSocket = mAdapter.listenUsingRfcommWithServiceRecord
                    ("SIM Access", BluetoothUuid.SAP.getUuid());
                //
                // TODO: Consider reusing the mServerSocket - it is intended
                // to be reused for multiple connections.
                //
                mServerSocket = mAdapter.
                        listenUsingRfcommOn(BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP);
                int channel = mServerSocket.getChannel();
                if (mSdpHandle >= 0) {
                    SdpManager.getDefaultManager().removeSdpRecord(mSdpHandle);
                    if (VERBOSE) Log.d(TAG, "Removing SDP record");
                }
                mSdpHandle = SdpManager.getDefaultManager().createSapsRecord(SDP_SAP_SERVICE_NAME,
                        mServerSocket.getChannel(), SDP_SAP_VERSION);

            } catch (IOException e) {
                Log.e(TAG, "Error create RfcommServerSocket ", e);
+51 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ import android.bluetooth.SdpMasRecord;
import android.bluetooth.SdpMnsRecord;
import android.bluetooth.SdpOppOpsRecord;
import android.bluetooth.SdpPseRecord;
import android.bluetooth.SdpSapsRecord;
import android.bluetooth.SdpRecord;
import android.content.Intent;
import android.os.Handler;
@@ -107,6 +108,9 @@ public class SdpManager {
    private native int sdpCreateOppOpsRecordNative(String serviceName,
            int rfcommChannel, int l2capPsm, int version, byte[] formats_list);

    private native int sdpCreateSapsRecordNative(String serviceName, int rfcommChannel,
            int version);

    private native boolean sdpRemoveSdpRecordNative(int record_id);


@@ -364,6 +368,31 @@ public class SdpManager {
        }
    }

    void sdpSapsRecordFoundCallback(int status, byte[] address, byte[] uuid,
            int rfcommCannelNumber,
            int profileVersion,
            String serviceName,
            boolean moreResults) {

        synchronized(mTrackerLock) {
            SdpSearchInstance inst = sSdpSearchTracker.getSearchInstance(address, uuid);
            SdpSapsRecord sdpRecord = null;
            if (inst == null) {
                Log.e(TAG, "sdpSapsRecordFoundCallback: Search instance is NULL");
                return;
            }
            inst.setStatus(status);
            if (status == AbstractionLayer.BT_STATUS_SUCCESS) {
                sdpRecord = new SdpSapsRecord(rfcommCannelNumber,
                                             profileVersion,
                                             serviceName);
            }
            if (D) Log.d(TAG, "UUID: " + Arrays.toString(uuid));
            if (D) Log.d(TAG, "UUID in parcel: " + ((Utils.byteArrayToUuid(uuid))[0]).toString());
            sendSdpIntent(inst, sdpRecord, moreResults);
        }
    }

    /* TODO: Test or remove! */
    void sdpRecordFoundCallback(int status, byte[] address, byte[] uuid,
            int size_record, byte[] record) {
@@ -591,6 +620,28 @@ public class SdpManager {
                 l2capPsm, version, formatsList);
    }

    /**
     * Create a server side Sim Access Profile Service Record.
     * Create the record once, and reuse it for all connections.
     * If changes to a record is needed remove the old record using {@link removeSdpRecord}
     * and then create a new one.
     * @param serviceName   The textual name of the service
     * @param rfcommChannel The RFCOMM channel that clients can connect to
     *                      (obtain from BluetoothServerSocket)
     * @param version       The Profile version number (As specified in the Bluetooth
     *                      SAP specification)
     * @return a handle to the record created. The record can be removed again
     *          using {@link removeSdpRecord}(). The record is not linked to the
     *          creation/destruction of BluetoothSockets, hence SDP record cleanup
     *          is a separate process.
     */
    public int createSapsRecord(String serviceName, int rfcommChannel, int version) {
        if (sNativeAvailable == false) {
            throw new RuntimeException(TAG + " sNativeAvailable == false - native not initialized");
        }
        return sdpCreateSapsRecordNative(serviceName, rfcommChannel, version);
    }

     /**
      * Remove a SDP record.
      * When Bluetooth is disabled all records will be deleted, hence there
+56 −31
Original line number Diff line number Diff line
@@ -30,8 +30,9 @@ import com.android.bluetooth.sap.SapServer;
public class SapServerTest extends AndroidTestCase {
    protected static String TAG = "SapServerTest";
    protected static final boolean D = true;
    private static final boolean rilTestModeEnabled = false; /* Set the RIL driver in test mode, where request stubs are used in stead of forwarding to the modem/sim */

    // Set the RIL driver in test mode, where request stubs are used instead
    // of forwarding to the Modem/SIM.
    private static final boolean rilTestModeEnabled = false;
    private Context mContext = null;

    public SapServerTest() {
@@ -176,16 +177,20 @@ public class SapServerTest extends AndroidTestCase {
            }
            sequencer.addStep(apdu7816Req, apdu7816Resp);

            SapMessage transferCardReaderStatusReq = new SapMessage(SapMessage.ID_TRANSFER_CARD_READER_STATUS_REQ);
            SapMessage transferCardReaderStatusReq =
                    new SapMessage(SapMessage.ID_TRANSFER_CARD_READER_STATUS_REQ);

            SapMessage transferCardReaderStatusResp = new SapMessage(SapMessage.ID_TRANSFER_CARD_READER_STATUS_RESP);
            SapMessage transferCardReaderStatusResp =
                    new SapMessage(SapMessage.ID_TRANSFER_CARD_READER_STATUS_RESP);
            transferCardReaderStatusResp.setResultCode(SapMessage.RESULT_OK);
            sequencer.addStep(transferCardReaderStatusReq, transferCardReaderStatusResp);

            SapMessage setTransportProtocolReq = new SapMessage(SapMessage.ID_SET_TRANSPORT_PROTOCOL_REQ);
            SapMessage setTransportProtocolReq =
                    new SapMessage(SapMessage.ID_SET_TRANSPORT_PROTOCOL_REQ);
            setTransportProtocolReq.setTransportProtocol(0x01); // T=1

            SapMessage setTransportProtocolResp = new SapMessage(SapMessage.ID_SET_TRANSPORT_PROTOCOL_RESP);
            SapMessage setTransportProtocolResp =
                    new SapMessage(SapMessage.ID_SET_TRANSPORT_PROTOCOL_RESP);
            setTransportProtocolResp.setResultCode(SapMessage.RESULT_OK);
            sequencer.addStep(setTransportProtocolReq, setTransportProtocolResp);

@@ -222,8 +227,12 @@ public class SapServerTest extends AndroidTestCase {
            SapMessage apduReq = new SapMessage(SapMessage.ID_TRANSFER_APDU_REQ);
            apduReq.setApdu(dummyBytes);

            SapMessage apduResp = null; /* expect no response as we send a SIM_RESET before the write APDU completes
                                            TODO: Consider adding a real response, and add an optional flag. */
            //
            // Expect no response as we send a SIM_RESET before the write APDU
            // completes.
            // TODO: Consider adding a real response, and add an optional flag.
            //
            SapMessage apduResp = null;
            index = sequencer.addStep(apduReq, apduResp);

            SapMessage resetReq = new SapMessage(SapMessage.ID_RESET_SIM_REQ);
@@ -249,7 +258,8 @@ public class SapServerTest extends AndroidTestCase {
     */
    public void testSapServerTimeouts() {
        Intent sapDisconnectIntent = new Intent(SapServer.SAP_DISCONNECT_ACTION);
        sapDisconnectIntent.putExtra(SapServer.SAP_DISCONNECT_TYPE_EXTRA, SapMessage.DISC_IMMEDIATE);
        sapDisconnectIntent.putExtra(
                SapServer.SAP_DISCONNECT_TYPE_EXTRA, SapMessage.DISC_IMMEDIATE);
        mContext = this.getContext();

        try {
@@ -298,7 +308,8 @@ public class SapServerTest extends AndroidTestCase {
        public class SeqStep {
            public ArrayList<SapMessage> requests = null;
            public ArrayList<SapMessage> responses = null;
            public int index = 0; /* requests with same index are executed in parallel (without waiting for a response) */
            public int index = 0; // Requests with same index are executed in
                                  // parallel without waiting for a response
            public SeqStep(SapMessage request, SapMessage response) {
                requests = new ArrayList<SapMessage>();
                responses = new ArrayList<SapMessage>();
@@ -328,7 +339,8 @@ public class SapServerTest extends AndroidTestCase {

        public SapSequencer() throws IOException {
            /* Setup the looper thread to handle messages */
            handlerThread = new HandlerThread("SapTestTimeoutHandler", android.os.Process.THREAD_PRIORITY_BACKGROUND);
            handlerThread = new HandlerThread("SapTestTimeoutHandler",
                    android.os.Process.THREAD_PRIORITY_BACKGROUND);
            handlerThread.start();
            Looper testLooper = handlerThread.getLooper();
            messageHandler = new Handler(testLooper, this);
@@ -339,7 +351,8 @@ public class SapServerTest extends AndroidTestCase {
            /* Create a SapServer. Fake the BtSocket using piped input/output streams*/
            inStream = new PipedInputStream(8092);
            outStream = new PipedOutputStream();
            sapServer = new SapServer(null, mContext, new PipedInputStream(outStream, 8092), new PipedOutputStream(inStream));
            sapServer = new SapServer(null, mContext, new PipedInputStream(outStream, 8092),
                    new PipedOutputStream(inStream));
            sapServer.start();
        }

@@ -364,15 +377,16 @@ public class SapServerTest extends AndroidTestCase {
         * @param response The response to EXPECT from the SAP server
         * @return The created step index, which can be used when adding events or actions.
         */
        public int addStep(SapMessage request, SapMessage response) { // TODO: should we add a step trigger? (in stead of just executing in sequence)
        public int addStep(SapMessage request, SapMessage response) {
            // TODO: should we add a step trigger? (in stead of just executing in sequence)
            SeqStep newStep = new SeqStep(request, response);
            sequence.add(newStep);
            return sequence.indexOf(newStep);
        }

        /**
         * Add a sub-step to a sequencer step. All requests added to the same index will be send to the
         * SapServer in the order added before listening for the response.
         * Add a sub-step to a sequencer step. All requests added to the same index will be send to
         * the SapServer in the order added before listening for the response.
         * The response order is not validated - hence for each response received the entire list of
         * responses in the step will be searched for a match.
         * @param index the index returned from addStep() to which the sub-step is to be added.
@@ -399,8 +413,9 @@ public class SapServerTest extends AndroidTestCase {
                if(step.requests != null) {
                    for(SapMessage request : step.requests) {
                        if(request != null) {
                            Log.i(TAG, "Writing request: " + SapMessage.getMsgTypeName(request.getMsgType()));
                            writeSapMessage(request, false); /* write the message without flushing */
                            Log.i(TAG, "Writing request: " +
                                    SapMessage.getMsgTypeName(request.getMsgType()));
                            writeSapMessage(request, false); // write the message without flushing
                        }
                    }
                    writeSapMessage(null, true); /* flush the pipe */
@@ -414,11 +429,13 @@ public class SapServerTest extends AndroidTestCase {
                    while(!done) {
                        for(SapMessage response : step.responses) {
                            if(response != null)
                                Log.i(TAG, "Waiting for the response: " + SapMessage.getMsgTypeName(response.getMsgType()));
                                Log.i(TAG, "Waiting for the response: " +
                                        SapMessage.getMsgTypeName(response.getMsgType()));
                        }
                        inMsg = readSapMessage();
                        if(inMsg != null)
                            Log.i(TAG, "Read message: " + SapMessage.getMsgTypeName(inMsg.getMsgType()));
                            Log.i(TAG, "Read message: " +
                                    SapMessage.getMsgTypeName(inMsg.getMsgType()));
                        else
                            assertTrue("Failed to read message.", false);

@@ -465,7 +482,8 @@ public class SapServerTest extends AndroidTestCase {
         */
        private boolean compareSapMessages(SapMessage received, SapMessage expected) {
            boolean retVal = true;
            if(expected.getCardReaderStatus() != -1 && received.getCardReaderStatus() != expected.getCardReaderStatus()) {
            if(expected.getCardReaderStatus() != -1 &&
                    received.getCardReaderStatus() != expected.getCardReaderStatus()) {
                Log.i(TAG, "received.getCardReaderStatus() != expected.getCardReaderStatus() "
                        + received.getCardReaderStatus() + " != " + expected.getCardReaderStatus());
                retVal = false;
@@ -477,7 +495,8 @@ public class SapServerTest extends AndroidTestCase {
            }
            if(received.getDisconnectionType() != expected.getDisconnectionType()) {
                Log.i(TAG, "received.getDisconnectionType() != expected.getDisconnectionType() "
                            + received.getDisconnectionType() +" != " + expected.getDisconnectionType());
                        + received.getDisconnectionType() + " != "
                        + expected.getDisconnectionType());
                retVal = false;
            }
            if(received.getMaxMsgSize() != expected.getMaxMsgSize()) {
@@ -502,27 +521,33 @@ public class SapServerTest extends AndroidTestCase {
            }
            if(received.getTransportProtocol() != expected.getTransportProtocol()) {
                Log.i(TAG, "received.getTransportProtocol() != expected.getTransportProtocol() "
                        + received.getTransportProtocol() +" != " + expected.getTransportProtocol());
                        + received.getTransportProtocol() + " != "
                        + expected.getTransportProtocol());
                retVal = false;
            }
            if(!Arrays.equals(received.getApdu(), expected.getApdu())) {
                Log.i(TAG, "received.getApdu() != expected.getApdu() "
                        + Arrays.toString(received.getApdu()) +" != " + Arrays.toString(expected.getApdu()));
                        + Arrays.toString(received.getApdu()) + " != "
                        + Arrays.toString(expected.getApdu()));
                retVal = false;
            }
            if(!Arrays.equals(received.getApdu7816(), expected.getApdu7816())) {
                Log.i(TAG, "received.getApdu7816() != expected.getApdu7816() "
                        + Arrays.toString(received.getApdu7816()) +" != " + Arrays.toString(expected.getApdu7816()));
                        + Arrays.toString(received.getApdu7816()) + " != "
                        + Arrays.toString(expected.getApdu7816()));
                retVal = false;
            }
            if(expected.getApduResp() != null && !Arrays.equals(received.getApduResp(), expected.getApduResp())) {
            if(expected.getApduResp() != null && !Arrays.equals(received.getApduResp(),
                    expected.getApduResp())) {
                Log.i(TAG, "received.getApduResp() != expected.getApduResp() "
                        + Arrays.toString(received.getApduResp()) +" != " + Arrays.toString(expected.getApduResp()));
                        + Arrays.toString(received.getApduResp()) + " != "
                        + Arrays.toString(expected.getApduResp()));
                retVal = false;
            }
            if(expected.getAtr() != null && !Arrays.equals(received.getAtr(), expected.getAtr())) {
                Log.i(TAG, "received.getAtr() != expected.getAtr() "
                        + Arrays.toString(received.getAtr()) +" != " + Arrays.toString(expected.getAtr()));
                        + Arrays.toString(received.getAtr()) + " != "
                        + Arrays.toString(expected.getAtr()));
                retVal = false;
            }
            return retVal;
+8 −1
Original line number Diff line number Diff line
@@ -110,6 +110,12 @@ public class SdpManagerTest extends AndroidTestCase {
            Log.i(TAG, "  Added record_handle=" + handles[record_id]);
            assertTrue(handles[record_id]>=0);
            if(record_id == count) break;

            handles[++record_id] = mManager.createSapsRecord(SDP_SERVER_NAME,
                    record_id, SDP_VERSION);
            Log.i(TAG, "  Added record_handle=" + handles[record_id]);
            assertTrue(handles[record_id]>=0);
            if (record_id == count) break;
        }
    }

@@ -159,7 +165,8 @@ public class SdpManagerTest extends AndroidTestCase {
        final String[] uuids = {BluetoothUuid.MAS.toString(),
                                BluetoothUuid.MNS.toString(),
                                BluetoothUuid.PBAP_PSE.toString(),
                                BluetoothUuid.ObexObjectPush.toString()};
                                BluetoothUuid.ObexObjectPush.toString(),
                                BluetoothUuid.SAP.toString()};
        final String uuids_str;
        final StringBuilder sb = new StringBuilder(uuids.length*2-1);
        for(String str : uuids) {
Loading