Zebra Android Scanner SDK

Zebra Android Scanner SDK 

1. What is Zebra Android Scanner SDK?

Zebra Android Scanner SDK is used to connect an Android app with Zebra barcode scanners.

Simple meaning:

Android App
   |
Zebra Scanner SDK
   |
Zebra Barcode Scanner

It helps Android apps:

- Pair scanner
- Connect scanner
- Receive barcode data
- Enable/disable scanner
- Beep scanner
- Control LED
- Handle scanner events
- Update firmware

Zebra’s official guide says the SDK helps developers integrate barcode scanner functionality into Android Studio projects and provides APIs for scanner features like LED, beeper, and firmware updates.

---

2. Real Use Cases

Use CaseExample
WarehouseScan product barcode
RetailScan item at billing counter
LogisticsScan package/tracking ID
ManufacturingScan component serial number
HealthcareScan patient wristband
InventoryScan stock item and update quantity
Field serviceScan asset tag

Example:

Worker scans product barcode
        |
Zebra scanner sends barcode to Android app
        |
App validates product
        |
App updates inventory/server

---

3. High-Level Flow

Android App
   |
Add Zebra Scanner SDK AAR
   |
Request Bluetooth permissions
   |
Initialize SDKHandler
   |
Set operation mode
   |
Subscribe scanner events
   |
Show Scan-To-Connect barcode
   |
Scanner scans pairing barcode
   |
Scanner connects to app
   |
App receives barcode event
   |
Process barcode data

---

4. Important Classes / Concepts

| Concept                                 | Meaning                               |
| --------------------------------------- | ------------------------------------- |
| `SDKHandler`                            | Main SDK object                       |
| `DCSScannerInfo`                        | Scanner information                   |
| `scannerId`                             | Unique scanner ID assigned by SDK     |
| `IDcsSdkApiDelegate`                    | Callback interface for scanner events |
| `dcssdkSetOperationalMode()`            | Set connection mode                   |
| `dcssdkSubsribeForEvents()`             | Subscribe to scanner events           |
| `dcssdkEstablishCommunicationSession()` | Connect scanner                       |
| `dcssdkTerminateCommunicationSession()` | Disconnect scanner                    |
| `dcssdkEventBarcode()`                  | Barcode received callback             |

Zebra docs show SDKHandler initialization, delegate setup, scanner detection, operation modes, event subscription, and barcode event callbacks through IDcsSdkApiDelegate.

---

5. Connection Types

Zebra scanner can connect mainly using:

1. Bluetooth Classic
2. Bluetooth Low Energy / BLE
3. SNAPI / Wired mode
4. Scan-To-Connect barcode

Zebra docs explain that the scanner can connect using SSI BT Classic discoverable barcode or Scan-To-Connect barcode. Scan-To-Connect is easier because one scan can establish connection with both the Android device and the app.

---

6. Scan-To-Connect Meaning

What is Scan-To-Connect?

Scan-To-Connect means your Android app displays a pairing barcode. The Zebra scanner scans that barcode and connects to the app.

Simple meaning:

App shows pairing barcode
        |
Scanner scans barcode
        |
Scanner connects to Android app

Diagram:

Android App Screen
   |
Shows Scan-To-Connect Barcode
   |
Zebra Scanner scans it
   |
Pairing + App Connection Done

---

7. Implementation Steps

Step 1: Add SDK AAR

Add Zebra Scanner SDK AAR file into your app module libs folder.

Example:

app/
   libs/
      barcode_scanner_library_v2.x.x.x.aar

Then add it as an implementation dependency.

Zebra docs show adding barcode_scanner_library_vx.x.x.x.aar into the app module libs directory and adding it as a Jar/AAR dependency.

---

Step 2: Add Permissions

<uses-permission
    android:name="android.permission.BLUETOOTH"
    android:maxSdkVersion="30" />

<uses-permission
    android:name="android.permission.BLUETOOTH_ADMIN"
    android:maxSdkVersion="30" />

<uses-permission
    android:name="android.permission.BLUETOOTH_SCAN"
    android:usesPermissionFlags="neverForLocation" />

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

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

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

Zebra docs list Bluetooth permissions including legacy Bluetooth permissions, Android 12+ Bluetooth permissions, and location permission, and also note that BLUETOOTH_SCAN, BLUETOOTH_CONNECT, and BLUETOOTH_ADVERTISE are runtime permissions.

---

Step 3: Request Runtime Permissions

Simple flow:

App starts
   |
Check Android version
   |
Request Bluetooth permissions
   |
If granted
   |
Initialize Zebra SDK

Interview point:

Do not initialize scanner connection flow before required Bluetooth permissions are granted.

---

Step 4: Initialize SDK

Java-style pseudocode:

// Pseudocode: actual SDK classes are Java-based

class ZebraScannerManager(
    private val context: Context
) {
    private var sdkHandler: SDKHandler? = null

    fun initialize() {
        if (sdkHandler == null) {
            sdkHandler = SDKHandler(context, true)
        }

        sdkHandler?.dcssdkSetDelegate(scannerDelegate)

        sdkHandler?.dcssdkEnableAvailableScannersDetection(true)

        sdkHandler?.dcssdkSetOperationalMode(
            DCSSDKDefs.DCSSDK_MODE.DCSSDK_OPMODE_BT_NORMAL
        )

        subscribeEvents()
    }

    private fun subscribeEvents() {
        var mask = 0

        mask = mask or DCSSDKDefs.DCSSDK_EVENT
            .DCSSDK_EVENT_SCANNER_APPEARANCE.value

        mask = mask or DCSSDKDefs.DCSSDK_EVENT
            .DCSSDK_EVENT_SCANNER_DISAPPEARANCE.value

        mask = mask or DCSSDKDefs.DCSSDK_EVENT
            .DCSSDK_EVENT_SESSION_ESTABLISHMENT.value

        mask = mask or DCSSDKDefs.DCSSDK_EVENT
            .DCSSDK_EVENT_SESSION_TERMINATION.value

        mask = mask or DCSSDKDefs.DCSSDK_EVENT
            .DCSSDK_EVENT_BARCODE.value

        sdkHandler?.dcssdkSubsribeForEvents(mask)
    }
}

Zebra docs show initialization through SDKHandler, setting the delegate, enabling scanner detection, setting operational modes such as BT LE, BT Normal, and SNAPI, and subscribing to scanner appearance, session, and barcode events.

---

8. Operation Modes

ModeMeaning
DCSSDK_OPMODE_BT_NORMALBluetooth Classic mode
DCSSDK_OPMODE_BT_LEBluetooth Low Energy mode
DCSSDK_OPMODE_SNAPIWired/SNAPI scanner mode

Simple memory:

BT_NORMAL = Bluetooth Classic
BT_LE     = Bluetooth Low Energy
SNAPI     = Wired scanner mode

---

9. Connect Scanner

Basic flow:

Get available scanner list
        |
Pick scanner
        |
Get scannerId
        |
Call establish communication session
        |
Receive session established callback

Pseudocode:

fun connectScanner() {
    val scanners = ArrayList<DCSScannerInfo>()

    sdkHandler?.dcssdkGetAvailableScannersList(scanners)

    if (scanners.isNotEmpty()) {
        val scannerId = scanners[0].scannerID

        // Run on background thread/coroutine
        sdkHandler?.dcssdkEstablishCommunicationSession(scannerId)
    }
}

Zebra docs use dcssdkGetAvailableScannersList() to fill scanner information and dcssdkEstablishCommunicationSession(scannerId) to make the scanner connection.

---

10. Disconnect Scanner

fun disconnectScanner() {
    val activeScanners = ArrayList<DCSScannerInfo>()

    sdkHandler?.dcssdkGetActiveScannersList(activeScanners)

    if (activeScanners.isNotEmpty()) {
        val scannerId = activeScanners[0].scannerID

        sdkHandler?.dcssdkTerminateCommunicationSession(scannerId)
    }
}

Zebra docs recommend using dcssdkTerminateCommunicationSession(scannerId) to disconnect the scanner instead of closing the entire SDK handler, so the same SDKHandler can be reused.

---

11. Receive Barcode Data

When barcode is scanned, SDK callback is triggered.

private val scannerDelegate = object : IDcsSdkApiDelegate {

    override fun dcssdkEventBarcode(
        barcodeData: ByteArray,
        barcodeType: Int,
        fromScannerID: Int
    ) {
        val scannedValue = String(barcodeData)

        Log.d("ZEBRA", "Barcode: $scannedValue")

        // Send to ViewModel / Repository Flow
    }

    override fun dcssdkEventCommunicationSessionEstablished(
        activeScanner: DCSScannerInfo
    ) {
        Log.d("ZEBRA", "Scanner connected")
    }

    override fun dcssdkEventCommunicationSessionTerminated(
        scannerID: Int
    ) {
        Log.d("ZEBRA", "Scanner disconnected")
    }
}

Zebra docs define dcssdkEventBarcode(byte[] barcodeData, int barcodeType, int fromScannerID) as the barcode event notification that provides scanned barcode data, barcode type, and scanner ID.

---

12. Enable / Disable Scanner

Use this when you want to control scanning from the app.

Enable scanner  = scanner can scan barcode
Disable scanner = scanner cannot scan barcode

Pseudocode:

fun enableScanner(scannerId: Int) {
    val inXml = """
        <inArgs>
            <scannerID>$scannerId</scannerID>
        </inArgs>
    """.trimIndent()

    val outXml = StringBuilder()

    sdkHandler?.dcssdkExecuteCommandOpCodeInXMLForScanner(
        DCSSDKDefs.DCSSDK_COMMAND_OPCODE.DCSSDK_DEVICE_SCAN_ENABLE,
        inXml,
        outXml
    )
}

Zebra docs show enabling the scanner using the DCSSDK_DEVICE_SCAN_ENABLE opcode and disabling barcode scanning using the corresponding scanner command.

---

13. SDK Close vs Scanner Disconnect

| Action                                           | Meaning                                     | When to Use                         |
| ------------------------------------------------ | ------------------------------------------- | ----------------------------------- |
| `dcssdkTerminateCommunicationSession(scannerId)` | Disconnect scanner session                  | Normal disconnect                   |
| `dcssdkClose()`                                  | Close SDK handler and cleanup SDK resources | App shutdown / SDK no longer needed |

Important:

For normal scanner disconnect, use terminate communication session.
For complete cleanup, use dcssdkClose().
Zebra docs state that `dcssdkClose()` unsubscribes SDK events, cleans resources, and closes the SDK instance; after calling it, SDK reinitialization is required for future events.

---

14. Recommended Android Architecture

For 11+ years interview, do not keep SDK code directly inside Activity.

Use this:

UI / Compose / Activity
        |
ViewModel
        |
Use Case
        |
Repository Interface
        |
Repository Implementation
        |
Zebra Scanner Data Source
        |
Zebra Scanner SDK
        |
Scanner Device

---

Package Structure

feature_scanner/
   presentation/
      ScannerScreen.kt
      ScannerViewModel.kt
      ScannerUiState.kt

   domain/
      repository/
         ScannerRepository.kt

      usecase/
         InitializeScannerSdkUseCase.kt
         ConnectScannerUseCase.kt
         DisconnectScannerUseCase.kt
         EnableScannerUseCase.kt
         DisableScannerUseCase.kt
         ObserveBarcodeUseCase.kt

   data/
      ZebraScannerRepositoryImpl.kt
      ZebraScannerDataSource.kt
      MockScannerDataSource.kt

---

15. UI State Example

sealed interface ScannerUiState {
    data object Idle : ScannerUiState
    data object PermissionRequired : ScannerUiState
    data object SdkInitializing : ScannerUiState
    data object WaitingForScanner : ScannerUiState
    data object Connecting : ScannerUiState
    data object Connected : ScannerUiState
    data object Disconnected : ScannerUiState
    data class BarcodeScanned(
        val value: String,
        val barcodeType: Int
    ) : ScannerUiState
    data class Error(val message: String) : ScannerUiState
}

---

16. Repository Interface

interface ScannerRepository {

    fun observeScannerState(): Flow<ScannerConnectionState>

    fun observeBarcodeData(): Flow<ScannedBarcode>

    suspend fun initializeSdk()

    suspend fun connectScanner()

    suspend fun disconnectScanner()

    suspend fun enableScanner()

    suspend fun disableScanner()

    suspend fun closeSdk()
}

---

17. Data Model

data class ScannedBarcode(
    val value: String,
    val barcodeType: Int,
    val scannerId: Int,
    val scannedAt: Long = System.currentTimeMillis()
)

---

18. ViewModel Example

class ScannerViewModel(
    private val scannerRepository: ScannerRepository
) : ViewModel() {

    private val _uiState =
        MutableStateFlow<ScannerUiState>(ScannerUiState.Idle)

    val uiState: StateFlow<ScannerUiState> = _uiState

    init {
        observeBarcode()
        observeScannerState()
    }

    fun initialize() {
        viewModelScope.launch {
            try {
                _uiState.value = ScannerUiState.SdkInitializing
                scannerRepository.initializeSdk()
                _uiState.value = ScannerUiState.WaitingForScanner
            } catch (e: Exception) {
                _uiState.value =
                    ScannerUiState.Error(e.message ?: "SDK initialization failed")
            }
        }
    }

    fun connect() {
        viewModelScope.launch {
            _uiState.value = ScannerUiState.Connecting
            scannerRepository.connectScanner()
        }
    }

    private fun observeBarcode() {
        viewModelScope.launch {
            scannerRepository.observeBarcodeData().collect { barcode ->
                _uiState.value = ScannerUiState.BarcodeScanned(
                    value = barcode.value,
                    barcodeType = barcode.barcodeType
                )
            }
        }
    }

    private fun observeScannerState() {
        viewModelScope.launch {
            scannerRepository.observeScannerState().collect { state ->
                _uiState.value = when (state) {
                    ScannerConnectionState.Connected -> ScannerUiState.Connected
                    ScannerConnectionState.Disconnected -> ScannerUiState.Disconnected
                    ScannerConnectionState.Waiting -> ScannerUiState.WaitingForScanner
                }
            }
        }
    }
}

---

19. Zebra Scanner SDK vs DataWedge

This is important for interviews.

DataWedge

DataWedge is available out-of-the-box on Zebra Android devices and can send captured barcode data to apps without directly using scanner APIs. It can output scanned data as keystrokes or intents and can be configured with profiles.

Scanner SDK

Scanner SDK is useful when you need direct control of an external Zebra scanner from your Android app.

TopicZebra Scanner SDKZebra DataWedge
Best forDirect scanner integration/controlQuick scan data capture
Coding requiredYesLess or no code
Works withZebra barcode scannersZebra Android mobile computers and peripherals
Control scannerYes, more controlLimited/configuration based
EventsSDK callbacksIntents/keystrokes
FeaturesConnect, disconnect, barcode, beeper, LED, firmwareCapture and route data
ArchitectureSDK wrapper/repositoryBroadcast receiver/profile
Interview keywordSDKHandler, IDcsSdkApiDelegateProfiles, Intent Output

Simple memory:

DataWedge = easiest way to get scan data on Zebra Android devices
Scanner SDK = more control over scanner connection and scanner features

---

20. Pros and Cons

Pros

ProsExplanation
Direct scanner controlConnect, disconnect, enable, disable
Barcode event callbacksApp receives barcode data directly
Supports scanner featuresBeeper, LED, firmware update
Good for enterprise appsWarehouse, retail, logistics
Supports external scannersUseful beyond built-in scanner flow

Cons

ConsExplanation
More implementation effortNeed SDK setup and lifecycle handling
Bluetooth permission complexityAndroid 12+ runtime permissions
Hardware dependencyNeed Zebra scanner for full testing
SDK-specific knowledgeNeed Zebra APIs and XML commands
More error handlingPairing, disconnect, reconnect, scanner unavailable

---

21. Common Mistakes

1. Calling Zebra SDK directly from Activity.
2. Not requesting Android 12+ Bluetooth permissions.
3. Not handling scanner disconnect.
4. Not unsubscribing/closing SDK resources.
5. Confusing DataWedge with Scanner SDK.
6. Not using background thread for connection/commands.
7. Assuming only one scanner is always connected.
8. Not validating scanned barcode before using it.
9. Logging sensitive barcode data.
10. Not testing scan failure and reconnect cases.

---

22. Interview Questions and Answers

Q1. What is Zebra Android Scanner SDK?

> Zebra Android Scanner SDK is used to connect and control Zebra barcode scanners from an Android app. It provides APIs for pairing, connecting, receiving barcode events, enabling/disabling scanning, beeper/LED operations, and firmware-related operations.

Q2. How do you connect a Zebra scanner?

> First I add the SDK AAR and required Bluetooth permissions. After runtime permission approval, I initialize SDKHandler, set the operation mode such as BT Classic or BLE, subscribe to scanner events, get available scanners, and call dcssdkEstablishCommunicationSession(scannerId).

Q3. How do you receive barcode data?

> I implement IDcsSdkApiDelegate and handle dcssdkEventBarcode(). This callback gives barcode bytes, barcode type, and scanner ID. I convert the byte array to a string, validate it, then pass it to ViewModel/domain layer.

Q4. What is Scan-To-Connect?

> Scan-To-Connect is a Zebra pairing approach where the Android app displays a pairing barcode. The scanner scans that barcode and connects to the Android device and app.

Q5. Difference between DataWedge and Scanner SDK?

> DataWedge is easier and usually requires less code because it routes captured data to apps through profiles, keystrokes, or intents. Scanner SDK is better when the app needs direct scanner control, scanner events, connection management, beeper/LED, or firmware operations.

Q6. How would you design this in Clean Architecture?

> I would keep Zebra SDK calls inside a data source. The repository would expose scanner state and barcode data as Flow. Use cases would handle connect, disconnect, enable, disable, and observe barcode. ViewModel would expose UI state using StateFlow.

Q7. How do you handle scanner disconnect?

> I handle session termination callbacks, update UI state, clear active scanner information, and allow user-controlled reconnect. For automatic reconnect, I use limited retry/backoff and avoid infinite background loops.

Q8. What permissions are required?

> For Android 12+, Bluetooth scan/connect/advertise permissions are required at runtime. For older Android versions, legacy Bluetooth permissions are used with maxSdkVersion=30. Location permission may also be needed depending on scan behavior.

---

23. Final Revision Diagram

Zebra Scanner SDK
   |
   +-- Add AAR
   +-- Add Bluetooth permissions
   +-- Initialize SDKHandler
   +-- Set operational mode
   +-- Subscribe events
   +-- Show Scan-To-Connect barcode
   +-- Connect scanner
   +-- Receive barcode callback
   +-- Process scanned data
   +-- Disconnect / close SDK

---

24. Final Interview Pitch

> Zebra Android Scanner SDK is used when an Android app needs direct control over Zebra barcode scanners. I would add the SDK AAR, request Bluetooth permissions, initialize SDKHandler, set the required operation mode, subscribe to scanner events, and connect using dcssdkEstablishCommunicationSession. Barcode data is received through dcssdkEventBarcode. For senior-level design, I would wrap the SDK inside a repository/data-source layer and expose scanner state and barcode events using Flow/StateFlow. I would also handle permission denial, scanner disconnect, SDK cleanup, reconnect, and barcode validation carefully.