Skip navigation links

Package com.acs.audiojack

Provides classes and interfaces for communicating with ACS audio jack readers on Android 4.0 (Ice Cream Sandwich) or above.

See: Description

Package com.acs.audiojack Description

Provides classes and interfaces for communicating with ACS audio jack readers on Android 4.0 (Ice Cream Sandwich) or above. Note that the reader uses the audio channels on your Android device. Your application must allow the following permission in the AndroidManifest.xml.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="...">

    ...

    <uses-permission android:name="android.permission.RECORD_AUDIO" />

    ...

</manifest>
To create and initialize a AudioJackReader object, get an instance of AudioManager by calling Context.getSystemService() method.
...

public class MainActivity extends Activity {

    ...

    private AudioManager mAudioManager;
    private AudioJackReader mReader;

    ...

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        ...

        mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
        mReader = new AudioJackReader(mAudioManager);

        ...
    }

    ...
}

...

Starting and stopping the reader

For Android 6.0 or later, you must request RECORD_AUDIO permission at run-time in order to use the reader.

To start an instance of the AudioJackReader object, use the start() method. If your application does not stop the reader properly, it will make other applications cannot use the audio channels. To stop the instance of the AudioJackReader object, use the stop() method. The following activity implements the required lifecycle methods.

...

public class MainActivity extends Activity {

    ...

    @Override
    protected void onResume() {
        super.onResume();

        /* Request record audio permission. */
        if (ContextCompat.checkSelfPermission(this,
                Manifest.permission.RECORD_AUDIO)
                != PackageManager.PERMISSION_GRANTED) {
            if (!mPermissionDenied) {
                ActivityCompat.requestPermissions(this,
                        new String[]{Manifest.permission.RECORD_AUDIO},
                        REQUEST_RECORD_AUDIO);
            } else {
                finish();
            }
        } else {
            mReader.start();
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        mReader.stop();
    }

    ...
}

...

The result will be returned from onRequestPermissionsResult(). After the permission was granted, the app can use the reader.

@Override
public void onRequestPermissionsResult(int requestCode,
                                       @NonNull String[] permissions,
                                       @NonNull int[] grantResults) {
    if (requestCode == REQUEST_RECORD_AUDIO) {
        if ((grantResults.length > 0)
                && (grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
            mReader.start();
        } else {
            mPermissionDenied = true;
        }
    } else {
        super.onRequestPermissionsResult(requestCode, permissions,
                grantResults);
    }
}

Resetting the reader

The sleep mode of reader is enabled by default. To use the reader, your application should call reset() method. If you register a callback using setOnResetCompleteListener() method, it will receive a notification after the operation is completed.

...

/* Reset the reader. */
mReader.reset();

...

/* Set the reset complete callback. */
mReader.setOnResetCompleteListener(
        new AudioJackReader.OnResetCompleteListener() {

    @Override
    public void onResetComplete(AudioJackReader reader) {

        /* TODO: Add your code here to process the notification. */
        ...
    }
});

...

Controlling the sleep mode

You can enable the sleep mode by calling sleep() method. If you register a callback using setOnResultAvailableListener() method, it will receive a notification after the operation is completed.

...

/* Enable the sleep mode. */
mReader.sleep();

...

/* Set the result callback. */
mReader.setOnResultAvailableListener(
        new AudioJackReader.OnResultAvailableListener() {

    @Override
    public void onResultAvailable(AudioJackReader reader, Result result) {

        /* TODO: Add your code here to process the notification. */
        ...
    }
});

...

Setting the sleep timeout

You can set the sleep timeout by calling setSleepTimeout() method. If you register a callback using setOnResultAvailableListener() method, it will receive a notification after the operation is completed.

...

/* Set the sleep timeout to 10 seconds. */
mReader.setSleepTimeout(10);

...

/* Set the result callback. */
mReader.setOnResultAvailableListener(
        new AudioJackReader.OnResultAvailableListener() {

    @Override
    public void onResultAvailable(AudioJackReader reader, Result result) {

        /* TODO: Add your code here to process the notification. */
        ...
    }
});

...

Getting the firmware version

To get the firmware version, you need to register a callback using setOnFirmwareVersionAvailableListener() method and send a request using getFirmwareVersion() method.

...

/* Get the firmware version. */
mReader.getFirmwareVersion();

...

/* Set the firmware version callback. */
mReader.setOnFirmwareVersionAvailableListener(
        new AudioJackReader.OnFirmwareVersionAvailableListener() {

    @Override
    public void onFirmwareVersionAvailable(AudioJackReader reader,
        String firmwareVersion) {

        /* TODO: Add your code here to process the firmware version. */
        ...
    }
});

...

Getting the status

To get the status, you need to register a callback using setOnStatusAvailableListener() method and send a request using getStatus() method.

...

/* Get the status. */
mReader.getStatus();

...

/* Set the status callback. */
mReader.setOnStatusAvailableListener(
        new AudioJackReader.OnStatusAvailableListener() {

    @Override
    public void onStatusAvailable(AudioJackReader reader, Status status) {

        /* TODO: Add your code here to process the status. */
        ...
    }
});

...

Receiving the track data

When you swipe a card, the reader notifies a track data and sends it through an audio channel to your Android device. To receive the notification and the track data, you need to register a callback using setOnTrackDataNotificationListener() and setOnTrackDataAvailableListener() method. You can check the track error using TrackData.getTrack1ErrorCode() and TrackData.getTrack2ErrorCode() methods. Note that the received TrackData object will be the instance of AesTrackData or DukptTrackData according to the settings. You must check the type of instance before accessing the object.

You can get the track data using AesTrackData.getTrackData(), DukptTrackData.getTrack1Data() and DukptTrackData.getTrack2Data() methods. Note that the track data of AesTrackData object is encrypted by AES while the track data of DukptTrackData object is encrypted by Triple DES. You must decrypt it before accessing the original track data.

After decrypting the track data of AesTrackData object, you can use Track1Data.fromByteArray(byte[]) and Track2Data.fromByteArray(byte[]) methods to decode the track data into fields. For the track data or masked track data of DukptTrackData object, you can use Track1Data.fromString(String) and Track2Data.fromString(String) methods.

...

/* Set the track data notification callback. */
mReader.setOnTrackDataNotificationListener(
    new AudioJackReader.OnTrackDataNotificationListener() {

    @Override
    public void onTrackDataNotification(AudioJackReader reader) {

        /* TODO: Add your code here to process the notification. */
        ...
    }
});

...

/* Set the track data callback. */
mReader.setOnTrackDataAvailableListener(
        new AudioJackReader.OnTrackDataAvailableListener() {

    @Override
    public void onTrackDataAvailable(AudioJackReader reader,
            TrackData trackData) {

        /* TODO: Add your code here to process the track data. */
        if ((trackData.getTrack1ErrorCode() != TrackData.TRACK_ERROR_SUCCESS)
                || (trackData.getTrack2ErrorCode() != TrackData.TRACK_ERROR_SUCCESS)) {

            /* Show the track error. */
            ...

            return;
        }

        if (trackData instanceof AesTrackData) {

            AesTrackData aesTrackData = (AesTrackData) trackData;

            ...

        } else if (trackData instanceof DukptTrackData) {

            DukptTrackData dukptTrackData = (DukptTrackData) trackData;

            ...
        }

        ...
    }
});

...

Receiving the raw data

If you want to access a raw data of a response, you can register a callback using setOnRawDataAvailableListener() method. Note that the raw data is not verified by CRC16 checksum.

...

/* Set the raw data callback. */
mReader.setOnRawDataAvailableListener(
        new AudioJackReader.OnRawDataAvailableListener() {

    @Override
    public void onRawDataAvailable(AudioJackReader reader, byte[] rawData) {

        /* TODO: Add your code here to process the raw data. */
        ...
    }
});

...

Working with the ICC

If your reader came with the ICC interface, you can operate the card using the following methods:

Before transmitting the APDU, you need to reset the card using power() method. The ATR string will be returned if the card is operated normally. Otherwise, it will throw ReaderException to indicate the error.

After resetting the card, the card state is changed to CARD_NEGOTIABLE or CARD_SPECIFIC. You cannot transmit the APDU if the card state is not equal to CARD_SPECIFIC. To select the protocol, invoke setProtocol() method with the preferred protocols.

After selecting the protocol, you can transmit the command APDU using transmit() method.

...

int slotNum = 0;
int action = AudioJackReader.CARD_WARM_RESET;
int timeout = 10 * 1000;    /* 10 seconds. */
byte[] atr = null;

int preferredProtocols = AudioJackReader.PROTOCOL_T0
        | AudioJackReader.PROTOCOL_T1;
int activeProtocol = 0;

byte[] commandApdu = { 0x00, (byte) 0x84, 0x00, 0x00, 0x08 };
byte[] responseApdu = null;

...

try {

    /* Reset the card. */
    atr = mReader.power(slotNum, action, timeout);
    if (atr != null) {

        /* Set the protocol. */
        activeProtocol = mReader.setProtocol(slotNum, preferredProtocols,
                timeout);

        /* Transmit the APDU. */
        responseApdu = mReader.transmit(slotNum, commandApdu, timeout);
    }

} catch (ReaderException e) {

    e.printStackTrace();
}

...

You can transmit the control command to the reader using control() method if the reader supports a set of escape commands.

...

int controlCode = AudioJackReader.IOCTL_CCID_ESCAPE;
byte[] controlCommand = { (byte) 0xE0, 0x00, 0x00, 0x18, 0x00 };
byte[] controlResponse = null;

...

try {

    /* Transmit the control command. */
    controlResponse = mReader.control(slotNum, controlCode, controlCommand,
            timeout);

} catch (ReaderException e) {

    e.printStackTrace();
}

...

Working with the PICC

If your reader came with the PICC interface, you can operate the card using the following methods:

Before transmitting the APDU, you need to power on the card using piccPowerOn() method. If you register a callback using setOnPiccAtrAvailableListener() method, you can receive the ATR string from the card.

To transmit the APDU, you can use piccTransmit() method. If you register a callback using setOnPiccResponseApduAvailableListener() method, you can receive the response APDU from the card.

After using the card, you can power off the card using piccPowerOff() method. If you register a callback using setOnResultAvailableListener() method, it will receive a notification after the operation is completed.

...

int timeout = 1;    /* 1 second. */
int cardType = PICC_CARD_TYPE_ISO14443_TYPE_A
        | PICC_CARD_TYPE_ISO14443_TYPE_B
        | PICC_CARD_TYPE_FELICA_212KBPS
        | PICC_CARD_TYPE_FELICA_424KBPS
        | PICC_CARD_TYPE_AUTO_RATS;
byte[] apdu = { 0x00, (byte) 0x84, 0x00, 0x00, 0x08 };

...

/* Power on the PICC. */
mReader.piccPowerOn(timeout, cardType);

...

/* Transmit the APDU. */
mReader.piccTransmit(timeout, apdu);

...

/* Power off the PICC. */
mReader.piccPowerOff();

...

/* Set the PICC ATR callback. */
mReader.setOnPiccAtrAvailableListener(
        new AudioJackReader.OnPiccAtrAvailableListener() {

    @Override
    public void onPiccAtrAvailable(AudioJackReader reader, byte[] atr) {

        /* TODO: Add your code here to process the ATR. */
        ...
    }
});

/* Set the PICC response APDU callback. */
mReader.setOnPiccResponseApduAvailableListener(
        new AudioJackReader.OnPiccResponseApduAvailableListener() {

    @Override
    public void onPiccResponseApduAvailable(AudioJackReader reader,
        byte[] responseApdu) {

        /* TODO: Add your code here to process the response APDU. */
        ...
    }
});

/* Set the result callback. */
mReader.setOnResultAvailableListener(
        new AudioJackReader.OnResultAvailableListener() {

    @Override
    public void onResultAvailable(AudioJackReader reader, Result result) {

        /* TODO: Add your code here to process the notification. */
        ...
    }
});

...
Since:
1.0
Skip navigation links

Copyright © 2013-2018, Advanced Card Systems Ltd. All rights reserved.