# Service Development Guide (/global/en/docs/Terminal/NHAL/Service-Development-Guide)

---

## Preface

This document provides a concise guide for device manufacturers and software engineers on how to integrate and implement the provided NHAL(Newland Hardware Abstraction Layer) interfaces for Android POS devices. It assumes familiarity with Android service development and focuses on the steps necessary to develop and deploy HAL-based device services.

For detailed API specifications, please refer to the official HAL API Reference.

---

## Service Implementation

### Import HAL Interface Definitions

1. Place the HAL interface AAR file into your project's `libs` directory.

![](https://docs.newlandnpt.us/assets/_shared/5d8a6c7d8b2e/import_api_aar.png)

2. Ensure your build configuration (e.g., Gradle) includes the AAR as a dependency, for example:

```groovy
dependencies {
    implementation fileTree(include: ['*.jar','*.aar'], dir: 'libs')
}
```

3. Sync your project to make the HAL interfaces available for use.

### Implement the Service

`IHalService` is the main entry point for the HAL service.

![](https://docs.newlandnpt.us/assets/_shared/a379eef8891c/api_structure.png)

Your service is responsible for exposing a binder interface that implements the `IHalService` defined in the AAR.

1. Create a Service class that extends Android’s `Service`.

```java
public class HalService extends Service {
    @Override
    public void onCreate() {
        super.onCreate();
    }
}
```

2. Create a Binder object that implements the `IHalService.Stub` interface, for example:

```java
private final IHalService.Stub binder = new IHalService.Stub() {
    @Override
    public IPos getPos(PosDeviceConfig posDeviceConfig) throws RemoteException {
        // Return IPos instance according to the device config
    }
    ...
};
```

3. Override the `onBind()` method of your service to return your binder implementation:

```java
@Override
public IBinder onBind(Intent intent) {
    return binder;
}
```

### Configure the service

To allow clients to connect to the service using a unified configuration, configure the service as described below:

1. **Declare the service component**：Add the service in the manifest, and set a fixed "action" for easier client discovery.

```xml
<service
    android:name=".HalService"
    android:exported="true"
    android:enabled="true">
    <intent-filter>
        <action android:name="com.pos.hal.POS_SERVICE" />
    </intent-filter>
</service>
```

2. **Set a fixed "applicationId"**: In your module’s `build.gradle`,  set the `applicationId` to the following fixed value:

```groovy
android {
    ...

    defaultConfig {
        applicationId "com.pos.hal.service"
        ...
    }
    ...
}
```

This ensures that clients can bind to the service using a known and fixed package name, regardless of the hardware vendor.

---

## POS Device Implementation

The main part of the HAL service consists of POS payment-related modules, which are provided via the `IPos` interface. `IPos` represents a POS device, providing access to its various functional modules as well as device information. You can implement `IPos` as needed, and then provide it to clients through the `getPos` method of `IHalService`.

1. Create your POS class and extend from `IPos.Stub`, for example:

```java
public class InternalPos extends IPos.Stub {
    // Built-in (onboard) device 
}

public class ExternalPos extends IPos.Stub {
    // Peripheral POS device connected via Bluetooth, serial port, or USB.
}
```

2. Implement the methods of the `IPos` interface, for example:

```java
public class InternalPos extends IPos.Stub {
    @Override
    public DeviceInfo getDeviceInfo() throws RemoteException {
        // Get device info and return
        ...
    }
    
    @Override
    public ICardReader getCardReader() throws RemoteException {
        return CardReader.getInstance();
    }
    
    @Override
    public IPinPad getPinPad() throws RemoteException {
        return PinPad.getInstance();
    }

    @Override
    public IEmv getEmv() throws RemoteException {
        return Emv.getInstance();
    }
    ...
}

public class ExternalPos extends IPos.Stub {
    @Override
    public DeviceInfo getDeviceInfo() throws RemoteException {
        // Get device info and return
        ...
    }
    
    @Override
    public ICardReader getCardReader() throws RemoteException {
        return ExtCardReader.getInstance();
    }
    
    @Override
    public IPinPad getPinPad() throws RemoteException {
        return ExtPinPad.getInstance();
    }

    @Override
    public IEmv getEmv() throws RemoteException {
        return ExtEmv.getInstance();
    }
    ...
}
```

3. Return `IPos` instance according to the device config, for example:

```java
private final IHalService.Stub binder = new IHalService.Stub() {
    @Override
    public IPos getPos(PosDeviceConfig posDeviceConfig) throws RemoteException {
        ...
        if (posDeviceConfig.getDeviceType() == PosDeviceType.INTERNAL) 
             return InternalPos.getInstance();
        }
        if (posDeviceConfig.getDeviceType() == PosDeviceType.EXTERNAL) 
             return ExternalPos.getInstance(posDeviceConfig);
        }
        ...
    }
};
```

---

## Functional Module Implementation

For each functional module interface (such as `ICardReader`, `IPinPad`, `IEmv`, etc.), provide a concrete implementation that connects to the underlying HAL logic.

For example:

```java
public class CardReader extends ICardReader.Stub {
    // Implement this module as a singleton
    private static class Holder {
        private static final CardReader INSTANCE = new CardReader();
    }

    public static CardReader getInstance() {
        return Holder.INSTANCE;
    }
    
    // Implement the HAL interface methods
    @Override
    public void detectCard(CardReaderParameters parameters, ICardReaderListener listener) throws RemoteException {
        ...
    }
    
    @Override
    public void cancelDetect() throws RemoteException {
        ...
    }
    
    @Override
    public boolean isCardPresent(CardPresentParameters params) throws RemoteException {
        ...
    }
    
    @Override
    public ICpuCard getCpuCard(CpuCardParameters cpuCardParameters) throws RemoteException {
        ...
    }
}
```

---

## Error Reporting

Error Reporting Strategy:

- For synchronous interfaces, errors are reported via exceptions.

- For asynchronous interfaces, errors are primarily reported via callbacks.

However, AIDL can only throw Android predefined `RuntimeException`s and does not support custom `RuntimeException` types. The following are the `RuntimeException`s that can currently be thrown by AIDL interfaces on Android:

- `SecurityException`

- `BadParcelableException`

- `IllegalArgumentException`

- `NullPointerException`

- `IllegalStateException`

- `NetworkOnMainThreadException`

- `UnsupportedOperationException`

- `ServiceSpecificException` (available from API level 26 and above)

Since `ServiceSpecificException` is only available on Android API 26 or later, service developers should choose appropriate exceptions to throw based on the target device’s Android version and compatibility requirements.

To facilitate error reporting in services, the NHAL provides the `HalException` class, which allows creation of exceptions encapsulating both an error code and an error message. For example:

```java
int errorCode = -1;
int detailedCode = -2;
String errorMessage = "Test exception.";

String exceptionMessage = HalException.createMessage(errorCode, detailedCode, errorMessage);
throw new UnsupportedOperationException(exceptionMessage);
```

---

## Service Deliverable

After implementing the HAL interface, you should produce the **service APK deliverable** for installation and deployment.