SDK杂记: Difference between revisions

From wizarPOS
No edit summary
No edit summary
 
(4 intermediate revisions by the same user not shown)
Line 4: Line 4:


HSMDevice hsm = (HSMDevice) POSTerminal.getInstance(context)
HSMDevice hsm = (HSMDevice) POSTerminal.getInstance(context)
.getDevice(POSTerminal.DEVICE_NAME_HSM);
.getDevice(POSTerminal.DEVICE_NAME_HSM);
hsm.open();
hsm.open();


// 单字段CSR(原接口)
// 单字段CSR(原接口)
X500Principal subject1 = new X500Principal("CN=www.test.com");
X500Principal subject1 = new X500Principal("CN=www.test.com");
byte[] csr1 = hsm.generateCSR("terminal", subject1);
byte[] csr1 = hsm.generateCSR("terminal", subject1);


Line 14: Line 17:
X500Principal subject2 = new X500Principal(
X500Principal subject2 = new X500Principal(
"CN=pay.test.com,O=TestPayCorp,OU=Security Dept,C=CN,ST=Guangdong,L=Shenzhen");
"CN=pay.test.com,O=TestPayCorp,OU=Security Dept,C=CN,ST=Guangdong,L=Shenzhen");
byte[] csr2 = hsm.generateCSR("terminal", subject2);
byte[] csr2 = hsm.generateCSR("terminal", subject2);


Line 22: Line 26:
==主动查询终端是否越界的接口(电子围栏相关)==
==主动查询终端是否越界的接口(电子围栏相关)==
cloudpossdkV1.8.2.10开始支持, TmsAgent-v5.4.50.3以上开始支持。
cloudpossdkV1.8.2.10开始支持, TmsAgent-v5.4.50.3以上开始支持。
com.cloudpos.TerminalSpec
com.cloudpos.TerminalSpec
/** * Requests a geofence status check by sending a broadcast to the Terminal. * * <p> * When this method is invoked, Terminal performs a geofence validation immediately. * The result is delivered asynchronously via broadcast intents. This method does not * return the result directly. * </p> * * <p><b>Broadcast Result Actions:</b></p> * <ul> * <li> * Device is out of the geofence: * com.coudpos.ACTION_LOCATION_OUT_OF_BOUND * </li> * <li> * Device has re-entered the geofence after being out of bounds: * com.coudpos.ACTION_LOCATION_IN_OF_BOUND * </li> * <li> * Geofence feature is not enabled on the device: * com.coudpos.ACTION_LOCATION_GEOFENCE_DISABLED * </li> * </ul> * * <p><b>Implementation Details:</b></p> * <ul> * <li>Uses Android broadcast mechanism with action-based filtering</li> * <li>Caller must register a {@link android.content.BroadcastReceiver} to receive results</li> * <li>Result is typically returned shortly after the request is triggered</li> * </ul> * * <p><b>Notes:</b></p> * <ul> * <li>Ensure Terminal is registered before invoking this method</li> * </ul>
/** * Requests a geofence status check by sending a broadcast to the Terminal. * * <p> * When this method is invoked, Terminal performs a geofence validation immediately. * The result is delivered asynchronously via broadcast intents. This method does not * return the result directly. * </p> * * <p><b>Broadcast Result Actions:</b></p> * <ul> * <li> * Device is out of the geofence: * com.coudpos.ACTION_LOCATION_OUT_OF_BOUND * </li> * <li> * Device has re-entered the geofence after being out of bounds: * com.coudpos.ACTION_LOCATION_IN_OF_BOUND * </li> * <li> * Geofence feature is not enabled on the device: * com.coudpos.ACTION_LOCATION_GEOFENCE_DISABLED * </li> * </ul> * * <p><b>Implementation Details:</b></p> * <ul> * <li>Uses Android broadcast mechanism with action-based filtering</li> * <li>Caller must register a {@link android.content.BroadcastReceiver} to receive results</li> * <li>Result is typically returned shortly after the request is triggered</li> * </ul> * * <p><b>Notes:</b></p> * <ul> * <li>Ensure Terminal is registered before invoking this method</li> * </ul>
*/
*/
Line 28: Line 34:
//Sample code:
//Sample code:
TerminalSpec terminalSpec = POSTerminal.getInstance(context).getTerminalSpec();
TerminalSpec terminalSpec = POSTerminal.getInstance(context).getTerminalSpec();
terminalSpec.requestGeofenceBroadcast();
terminalSpec.requestGeofenceBroadcast();


广播接收:
广播接收:
- com.coudpos.ACTION_LOCATION_OUT_OF_BOUND — 终端出围栏
- com.coudpos.ACTION_LOCATION_OUT_OF_BOUND — 终端出围栏
- com.coudpos.ACTION_LOCATION_IN_OF_BOUND — 终端入围栏
- com.coudpos.ACTION_LOCATION_IN_OF_BOUND — 终端入围栏
- com.coudpos.ACTION_LOCATION_GEOFENCE_DISABLED — 围栏未启用
- com.coudpos.ACTION_LOCATION_GEOFENCE_DISABLED — 围栏未启用
<receiver
<receiver
android:name="com.cloudpos.api.receiver.LocationReceiver"
android:name="com.cloudpos.api.receiver.LocationReceiver"
android:enabled="true"
android:enabled="true"
android:exported="true">
android:exported="true">
<intent-filter>
<intent-filter>
<action android:name="com.coudpos.ACTION_LOCATION_OUT_OF_BOUND" />
<action android:name="com.coudpos.ACTION_LOCATION_OUT_OF_BOUND" />
</intent-filter>
</intent-filter>
<intent-filter>
<intent-filter>
<action android:name="com.coudpos.ACTION_LOCATION_IN_OF_BOUND" />
<action android:name="com.coudpos.ACTION_LOCATION_IN_OF_BOUND" />
</intent-filter>
</intent-filter>
<intent-filter>
<intent-filter>
<action android:name="com.coudpos.ACTION_LOCATION_GEOFENCE_DISABLED" />
<action android:name="com.coudpos.ACTION_LOCATION_GEOFENCE_DISABLED" />
</intent-filter>
</intent-filter>
</receiver>
</receiver>
==对于在Q1K/Q2pro上,persist.wp.hw.customerdisplay值可能是size:240x320或者size:272x480,由驱动负责设置。SDK提供获取接口来解析这个属性:
android.util.Size CustomerDisplayDevice.getSize();==
cloudpossdkV1.8.2.10_Standard.aar以上开始支持。
Q1K系列,需要bootloader0857+支持 #40099
<uses-permission android:name="android.permission.CLOUDPOS_CUSTOMER_DISPLAY"/>
com.cloudpos.customerdisplay
CustomerDisplayDevice device = (CustomerDisplayDevice) POSTerminal.getInstance(context).getDevice(POSTerminal.DEVICE_NAME_CUSTOMER_DISPLAY);
device.open();
device.close();
==logcat打包接口==
需要权限
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE" />
接口实现
ILogDevice device = LogDeviceImpl.getInstance(context);
示例代码:
public void test() {
if(th null || th.getState() Thread.State.TERMINATED){
th = new Thread(new Runnable() {
@Override
public void run() {
boolean result ;
ILogDevice logDevice = LogDeviceImpl.getInstance(mContext);
// result = logDevice.packageLog(1, "/sdcard/test/1_logs.zip");
// Logger.debug("run 1_logs result (%s)", result);
result = logDevice.packageLogcats(3, "/sdcard/test/3_logcats.zip");
Logger.debug("run 3_logcats result (%s)", result);
long endTime = System.currentTimeMillis();
long startTime = endTime - 12 * 60 * 60 * 1000;
result = logDevice.packageLogcats(startTime, endTime, "/sdcard/test/11_hours_logcats.zip");
Logger.debug("run 11_hours_logcats result (%s)", result);
}
});
th.start();
}
}
==属性ro.wp.antenna.position,0表示内置,1表示外置==
提供JavaSDK的底层c的接口部分,每次被调用时,同时设置persist.wp.antenna.position确保信息持久化。
cloudpossdkV1.8.1.1 验证ok
需要固件支持,Q3MINI-V(pcbc)system1072+ #40676
<uses-permission android:name="android.permission.CLOUDPOS_MCUCTL" />
AntennaDevice device = (AntennaDevice) POSTerminal.getInstance(context).getDevice(POSTerminal.DEVICE_NAME_ANTENNA);
device.open();
device.getAntennaMode();
device.setAntennaMode(AntennaDevice.AntennaMode.INTERNAL);
device.close();
==On Android 10 and above, background services that want to start other services and activities must have the following permissions.==
<uses-permission android:name="android.permission.START_ACTIVITIES_FROM_BACKGROUND" />
<uses-permission android:name="android.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND" />
==电子钱包接口说明==
The writeValue method is only used when initialize to the card for the first time. For subsequent interactions with the card, the decreaseValue and increaseValue methods need to be called. According to the rules of different cards, before performing the addition or subtraction operation, the verifykeya or verifykeyb methods should be called.
==selectPinblockFormat==
对于AES key, 默认是4, 而且只能是4, 也可以通过接口设置4,其他不支持.
int selectPinblockFormat(int nPinblockFormat)
                  throws DeviceException
Selects the pinblock format to use. Default format is ISO0. Notes on Pinblock Format Support and Handling: - Supported Formats: The HAL layer supports ISO0, ISO2, ISO4, and SM4 formats. - Case 0: ISO2 is exclusively for IC card offline PIN functions. The pinblock is stored in the hardware security module (HSM) and is inaccessible to the HAL layer, as it is not encrypted. - Case 1: When a key with a specific algorithm is injected, if the algorithm doesn't match the pinblock format, the HSM returns an error. A match leads to successful processing. - Case 2: Injecting a key without specifying an algorithm allows the HSM to choose the pinblock format based on your selection. - Case 3: If no pinblock format is selected, the HSM chooses one based on the algorithm of the injected key.
Parameters:
nPinblockFormat - Format index (0~5, ISO0/ISO1/ISO2/ISO3/ISO4/SM4).
Returns:
Negative for error, non-negative for success.
Throws:
DeviceException - for standard reasons as documented in DeviceException.
==emv_trans_initialize()==
Before each transaction is initiated, the emv_trans_initialize() interface should be called to clear the previous data and set the parameters for the new transaction (such as TransAmount/OtherAmount/Trans Date/Time/Trace/Trans Type...)
==exitEMVKernel()==
It should be emphasized that exitEMVKernel() should be deprecated and should never be called at any time.
==pinpad写密文主密钥接口的checkvalue的计算方法==
用解出来的新主密钥对8个0x00做3des加密取前4个当校验值
==算pinblock的时候报-81152(No data available)==
原因调用JNI层calculate pinblock之前要先select key, 不调用select key, 就会报这个错误。
JavaSDK的方法已经封装了selectkey, 所以没有问题
==注入主密钥的方式汇总==
两层密钥体系下:
1. 手输主密钥,测试阶段用,参考https://sdkwiki.wizarpos.com/index.php?title=%E6%89%8B%E5%B7%A5%E6%B3%A8%E5%85%A5%E4%B8%BB%E5%AF%86%E9%92%A5%EF%BC%8C%E5%AE%A2%E6%88%B7%E8%B0%83%E8%AF%95%E6%97%B6%E7%94%A8
2. 母POS注入,参考https://smartpossdk.gitbook.io/cloudpossdk/faq/key-injection/use-tmk-keyloader-pos
3. RKMS,以及远程注入主密钥demo开发系统,参考https://smartpossdk.gitbook.io/cloudpossdk/faq/key-injection/understand-remote-key-injection
4. SDK接口,不推荐,但是开发已经提供,有客户如果需要,也可以给。
1)reset master key to 16 bytes 0x38, then call updateMaster key, updateMasterKey(int masterKeyID, byte[] oldMasterKey, byte[] newMasterKey)
2) 只在最开始的时候reset master key, 调用updateMasterKey(int masterKeyID, byte[] oldMasterKey, byte[] newMasterKey), 应用里存储新的
master key, 作为下次的更新时候传入的oldMaster key. 应用存储的key也可以使用HSM密钥加密存储,保证安全。
三层密钥体系下:
传输密钥需要使用母POS, RKMS, 或者远程注入主密钥的接口,或者SDK特殊给客户定制的更新传输密钥的接口注入。
主密钥使用SDK提供的接口注入。updateMasterKeyWithCheck(int masterKeyID, byte[] cipherNewMasterKey, byte[] checkValue, int algoCheckValue)
==updateTransferKey仅支持visiontek,GSS==
GSS是新加的,需要配合使用如下的SDK和aar。
cloudpossdkV1.7.7.18_gss.aar 验证ok
需配合agentAssistant 2.15.191+使用
logo=gss或者visiontek
visiontek以前就支持。配合使用visiontek的aar包
<uses-permission android:name="android.permission.CLOUDPOS_PIN_UPDATE_TRANSFER_KEY" />
com.cloudpos.advance.ext.pinpad
IPINPadDevice iPinPadDevice = POSTerminalAdvance.getInstance().getPINPadDevice();
iPinPadDevice.open(context);
iPinPadDevice.updateTransferKey(slot,arryCipherNewKey);
iPinPadDevice.close();
==安卓6和7默认是NMEA串口,安卓12以后默认是ACM串口==
==Mifare card restoreValue和 transferValue的用法==
restorevalue, transfervalue一般用作备份数据。
在同一个扇区操作。
在扇区的不同block之间备份,首先block必须使用writevalue初始化成电子钱包。
同一个扇区的不同block, verify一次就可以。
调用逻辑:open--verfiy--restorevalue--transfervalue--close。
restorevalue和transfervalue之间做verify,transfervalue失败。
==ExtBoardDevice调用方法==
ExtBoardDevice extBoardDevice = (ExtBoardDevice) POSTerminal.getInstance(context).getDevice(POSTerminal.DEVICE_NAME_EXT_BOARD);
extBoardDevice.open();
extBoardDevice.close();
<uses-permission android:name="android.permission.CLOUDPOS_SERIAL" />
<uses-permission android:name="android.permission.CLOUDPOS_VIRTUAL_SERIAL_PORT" />
==MDB factor decimal问题==
一般factor设置1, decimal设置2. decimal设置成2的含义:因为mdb不能传输小数位数,这个设置可以让mdb传输带两个小数位数的数值,比如售货机设置0.01, 那么在协议命令里,可以设置值为1, 这样因为decimal为2, 所以实际的值是0.01. 有些售货机默认都是2, 不接受我们协议去更改。所以我们这边比如测试OPC的时候就设置为2. 售货机改不改都没关系。如果售货机会有其他值,那么再根据实际情况调整。
==普通应用截屏接口==
cloudpossdkV1.7.6.7以上
WizarViewAgentAssistant-v2.15.183+
com.cloudpos.advance.ext.system
ISystemDevice iSystemDevice = POSTerminalAdvance.getInstance().getSystemDevice();
iSystemDevice.open(context);
iSystemDevice .screenshot();
iSystemDevice .getScreenshotInPNG();
iSystemDevice.close();
<uses-permission android:name="android.permission.READ_FRAME_BUFFER" />
==wifi hotspot的设置API==
cloudpossdkV1.7.6.7以上
WizarViewAgentAssistant-v2.15.179+
com.cloudpos.advance.ext.system.network
IWifiDevice iWifiDevice = POSTerminalAdvance.getInstance().getSystemDevice().getNetworkManager().getWifiManager();
iWifiDevice.open(context);
iWifiDevice.startWifiAp("wifitest","abcdefgh","WPA2_PSK",false);
iWifiDevice.stopWifiAp();
iWifiDevice.close();
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
==检查终端是否root的接口==
cloudpossdkV1.7.6.7及以上
WizarViewAgentAssistant-v2.15.164+
com.cloudpos.advance.ext.system
ISystemDevice iSystemDevice = POSTerminalAdvance.getInstance().getSystemDevice();
iSystemDevice.open(context);
iSystemDevice .isTerminalRooted();
iSystemDevice.close();
==添加wifi证书,IUserCredentials使用==
cloudpossdkV1.7.6.10 以上开始支持
WizarViewAgentAssistant-2.15.183以上开始
com.cloudpos.advance.ext.system.security
IUserCredentials iUserCredentials = POSTerminalAdvance.getInstance().getSystemDevice().getUserCredentialsManager();
iUserCredentials.open(context);
iUserCredentials.installCaCert(CaCertBuffer);
iUserCredentials.existCaCert(CaCertAlias);
iUserCredentials.getCaCert(CaCertAlias);
iUserCredentials.deleteCaCert(CaCertAlias);
iUserCredentials.installKeyPair(KeyPairAlias,type,privateKeyBuffer,certBuffer);
iUserCredentials.existKeyPair(KeyPairAlias);
iUserCredentials.removeKeyPair(KeyPairAlias);
iUserCredentials.clearCredentials();
iUserCredentials.close();
<uses-permission android:name="android.permission.CLOUDPOS_USERCERTIFICATE" />
==功能键唤醒终端(waking up terminal by function key )==
cloudpossdkV1.7.7.3以上
WizarViewAgentAssistant-v2.15.186+
com.cloudpos.advance.ext.system
ISystemDevice iSystemDevice = POSTerminalAdvance.getInstance().getSystemDevice();
iSystemDevice.open(context);
iSystemDevice . setButtonWakeup (true);
iSystemDevice . isButtonWakeup ();
iSystemDevice.close();
<uses-permission android:name="android.permission.WRITE_POS_SETTINGS" />
==普通应用kill支付应用接口==
/** * Force-stops a payment application.
* * <p>This API terminates the target process immediately and clears its running state. * Only built-in/system payment applications are allowed to be terminated by this method. * Calling this on non-built-in apps will return {@code false} without performing any action.</p> * *
@param pkgName the package name of the payment application * @return {@code true} if the app process was successfully terminated; * {@code false} otherwise * * @requiresPermission android.permission.CLOUDPOS_APP_MANAGER
*/
boolean stopApp(String pkgName);
cloudpossdkV1.7.7.18_Standard.aar及以上
agentAssistant 2.15.190及以上
<uses-permission android:name="android.permission.CLOUDPOS_MANAGE_APP" />
ISystemDevice iSystemDevice = POSTerminalAdvance.getInstance().getSystemDevice();
iSystemDevice.open(context);
iSystemDevice.stopApp("com.example.paytest");
iSystemDevice.close();
==获取打印以及卡片最后使用时间的应用接口==
/** * Gets the last time the specified device was accessed.
* * @param deviceName the unique name of the device *
@return the last used time in milliseconds since epoch, * or -1 if the device has never been used * see : System.currentTimeMillis() *
@throws DeviceException for standard reasons as documented in {@link DeviceException NO_IMPLEMENT}.
*/
abstract public long getDeviceLastAccessTime(String deviceName) throws DeviceException;
cloudpossdkV1.7.7.18_Standard.aar 验证ok
需配合agent 5.4.43+使用
system也有更新,解决RF时间不准确问题(例如Q3MU system1042+)
long result = POSTerminal.getInstance(context).getDeviceLastAccessTime(deviceName);
==Admin/user登录/退出接口==
cloudpossdkV1.7.7.18_Standard.aar及以上
需配合 possetting 1.32.208及以上
添加权限
<uses-permission android:name="android.permission.USER_PWD" />
<uses-permission android:name="android.permission.ADMIN_PWD" />
com.cloudpos.advance.ext.system.login
ILoginDevice iLoginDevice = POSTerminalAdvance.getInstance().getSystemDevice().getLoginManager();
iLoginDevice.open(context);

Latest revision as of 09:23, 3 June 2026

在生成CSR的时候,可以输入证书的常用域,包括:countryName, stateOrProvinceName, localityName, organizationName, organizationUnitName, commonName和emailAddress

cloudpossdkV1.8.1.5开始支持 com.cloudpos.hsm.HSMDevice

HSMDevice hsm = (HSMDevice) POSTerminal.getInstance(context)

.getDevice(POSTerminal.DEVICE_NAME_HSM);

hsm.open();

// 单字段CSR(原接口) X500Principal subject1 = new X500Principal("CN=www.test.com");

byte[] csr1 = hsm.generateCSR("terminal", subject1);

// 多字段CSR(#40841新支持CN/O/OU/C/ST/L) X500Principal subject2 = new X500Principal( "CN=pay.test.com,O=TestPayCorp,OU=Security Dept,C=CN,ST=Guangdong,L=Shenzhen");

byte[] csr2 = hsm.generateCSR("terminal", subject2);

hsm.close();

权限:CLOUDPOS_SAFE_MODULE 前置条件:HSM需存在公私钥对(例如:terminal别名)

主动查询终端是否越界的接口(电子围栏相关)

cloudpossdkV1.8.2.10开始支持, TmsAgent-v5.4.50.3以上开始支持。

com.cloudpos.TerminalSpec

/** * Requests a geofence status check by sending a broadcast to the Terminal. * *

* When this method is invoked, Terminal performs a geofence validation immediately. * The result is delivered asynchronously via broadcast intents. This method does not * return the result directly. *

* *

Broadcast Result Actions:

*

    *
  • * Device is out of the geofence: * com.coudpos.ACTION_LOCATION_OUT_OF_BOUND *
  • *
  • * Device has re-entered the geofence after being out of bounds: * com.coudpos.ACTION_LOCATION_IN_OF_BOUND *
  • *
  • * Geofence feature is not enabled on the device: * com.coudpos.ACTION_LOCATION_GEOFENCE_DISABLED *
  • *

* *

Implementation Details:

*

    *
  • Uses Android broadcast mechanism with action-based filtering
  • *
  • Caller must register a {@link android.content.BroadcastReceiver} to receive results
  • *
  • Result is typically returned shortly after the request is triggered
  • *

* *

Notes:

*

    *
  • Ensure Terminal is registered before invoking this method
  • *
  • /

requestGeofenceBroadcast() //Sample code: TerminalSpec terminalSpec = POSTerminal.getInstance(context).getTerminalSpec();

terminalSpec.requestGeofenceBroadcast();

广播接收: - com.coudpos.ACTION_LOCATION_OUT_OF_BOUND — 终端出围栏

- com.coudpos.ACTION_LOCATION_IN_OF_BOUND — 终端入围栏

- com.coudpos.ACTION_LOCATION_GEOFENCE_DISABLED — 围栏未启用

<receiver android:name="com.cloudpos.api.receiver.LocationReceiver" android:enabled="true" android:exported="true">

<intent-filter> <action android:name="com.coudpos.ACTION_LOCATION_OUT_OF_BOUND" /> </intent-filter> <intent-filter>

<action android:name="com.coudpos.ACTION_LOCATION_IN_OF_BOUND" /> </intent-filter> <intent-filter>

<action android:name="com.coudpos.ACTION_LOCATION_GEOFENCE_DISABLED" /> </intent-filter> </receiver> ==对于在Q1K/Q2pro上,persist.wp.hw.customerdisplay值可能是size:240x320或者size:272x480,由驱动负责设置。SDK提供获取接口来解析这个属性: android.util.Size CustomerDisplayDevice.getSize();== cloudpossdkV1.8.2.10_Standard.aar以上开始支持。

Q1K系列,需要bootloader0857+支持 #40099

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

com.cloudpos.customerdisplay

CustomerDisplayDevice device = (CustomerDisplayDevice) POSTerminal.getInstance(context).getDevice(POSTerminal.DEVICE_NAME_CUSTOMER_DISPLAY);

device.open();

device.close();

logcat打包接口

需要权限

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

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

接口实现 ILogDevice device = LogDeviceImpl.getInstance(context);

示例代码:

public void test() {

if(th null || th.getState() Thread.State.TERMINATED){

th = new Thread(new Runnable() {

@Override

public void run() {

boolean result ;

ILogDevice logDevice = LogDeviceImpl.getInstance(mContext);

// result = logDevice.packageLog(1, "/sdcard/test/1_logs.zip");

// Logger.debug("run 1_logs result (%s)", result);

result = logDevice.packageLogcats(3, "/sdcard/test/3_logcats.zip");

Logger.debug("run 3_logcats result (%s)", result);

long endTime = System.currentTimeMillis();

long startTime = endTime - 12 * 60 * 60 * 1000;

result = logDevice.packageLogcats(startTime, endTime, "/sdcard/test/11_hours_logcats.zip");

Logger.debug("run 11_hours_logcats result (%s)", result); } }); th.start(); } }

属性ro.wp.antenna.position,0表示内置,1表示外置

提供JavaSDK的底层c的接口部分,每次被调用时,同时设置persist.wp.antenna.position确保信息持久化。

cloudpossdkV1.8.1.1 验证ok

需要固件支持,Q3MINI-V(pcbc)system1072+ #40676

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

AntennaDevice device = (AntennaDevice) POSTerminal.getInstance(context).getDevice(POSTerminal.DEVICE_NAME_ANTENNA);

device.open();

device.getAntennaMode();

device.setAntennaMode(AntennaDevice.AntennaMode.INTERNAL);

device.close();

On Android 10 and above, background services that want to start other services and activities must have the following permissions.

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

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

电子钱包接口说明

The writeValue method is only used when initialize to the card for the first time. For subsequent interactions with the card, the decreaseValue and increaseValue methods need to be called. According to the rules of different cards, before performing the addition or subtraction operation, the verifykeya or verifykeyb methods should be called.

selectPinblockFormat

对于AES key, 默认是4, 而且只能是4, 也可以通过接口设置4,其他不支持.

int selectPinblockFormat(int nPinblockFormat)

                 throws DeviceException

Selects the pinblock format to use. Default format is ISO0. Notes on Pinblock Format Support and Handling: - Supported Formats: The HAL layer supports ISO0, ISO2, ISO4, and SM4 formats. - Case 0: ISO2 is exclusively for IC card offline PIN functions. The pinblock is stored in the hardware security module (HSM) and is inaccessible to the HAL layer, as it is not encrypted. - Case 1: When a key with a specific algorithm is injected, if the algorithm doesn't match the pinblock format, the HSM returns an error. A match leads to successful processing. - Case 2: Injecting a key without specifying an algorithm allows the HSM to choose the pinblock format based on your selection. - Case 3: If no pinblock format is selected, the HSM chooses one based on the algorithm of the injected key. Parameters: nPinblockFormat - Format index (0~5, ISO0/ISO1/ISO2/ISO3/ISO4/SM4). Returns: Negative for error, non-negative for success. Throws: DeviceException - for standard reasons as documented in DeviceException.

emv_trans_initialize()

Before each transaction is initiated, the emv_trans_initialize() interface should be called to clear the previous data and set the parameters for the new transaction (such as TransAmount/OtherAmount/Trans Date/Time/Trace/Trans Type...)

exitEMVKernel()

It should be emphasized that exitEMVKernel() should be deprecated and should never be called at any time.

pinpad写密文主密钥接口的checkvalue的计算方法

用解出来的新主密钥对8个0x00做3des加密取前4个当校验值

算pinblock的时候报-81152(No data available)

原因调用JNI层calculate pinblock之前要先select key, 不调用select key, 就会报这个错误。

JavaSDK的方法已经封装了selectkey, 所以没有问题

注入主密钥的方式汇总

两层密钥体系下:

1. 手输主密钥,测试阶段用,参考https://sdkwiki.wizarpos.com/index.php?title=%E6%89%8B%E5%B7%A5%E6%B3%A8%E5%85%A5%E4%B8%BB%E5%AF%86%E9%92%A5%EF%BC%8C%E5%AE%A2%E6%88%B7%E8%B0%83%E8%AF%95%E6%97%B6%E7%94%A8

2. 母POS注入,参考https://smartpossdk.gitbook.io/cloudpossdk/faq/key-injection/use-tmk-keyloader-pos

3. RKMS,以及远程注入主密钥demo开发系统,参考https://smartpossdk.gitbook.io/cloudpossdk/faq/key-injection/understand-remote-key-injection

4. SDK接口,不推荐,但是开发已经提供,有客户如果需要,也可以给。

1)reset master key to 16 bytes 0x38, then call updateMaster key, updateMasterKey(int masterKeyID, byte[] oldMasterKey, byte[] newMasterKey)

2) 只在最开始的时候reset master key, 调用updateMasterKey(int masterKeyID, byte[] oldMasterKey, byte[] newMasterKey), 应用里存储新的 master key, 作为下次的更新时候传入的oldMaster key. 应用存储的key也可以使用HSM密钥加密存储,保证安全。

三层密钥体系下:

传输密钥需要使用母POS, RKMS, 或者远程注入主密钥的接口,或者SDK特殊给客户定制的更新传输密钥的接口注入。

主密钥使用SDK提供的接口注入。updateMasterKeyWithCheck(int masterKeyID, byte[] cipherNewMasterKey, byte[] checkValue, int algoCheckValue)

updateTransferKey仅支持visiontek,GSS

GSS是新加的,需要配合使用如下的SDK和aar。

cloudpossdkV1.7.7.18_gss.aar 验证ok

需配合agentAssistant 2.15.191+使用

logo=gss或者visiontek

visiontek以前就支持。配合使用visiontek的aar包

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

com.cloudpos.advance.ext.pinpad

IPINPadDevice iPinPadDevice = POSTerminalAdvance.getInstance().getPINPadDevice();

iPinPadDevice.open(context);

iPinPadDevice.updateTransferKey(slot,arryCipherNewKey);

iPinPadDevice.close();

安卓6和7默认是NMEA串口,安卓12以后默认是ACM串口

Mifare card restoreValue和 transferValue的用法

restorevalue, transfervalue一般用作备份数据。

在同一个扇区操作。

在扇区的不同block之间备份,首先block必须使用writevalue初始化成电子钱包。

同一个扇区的不同block, verify一次就可以。

调用逻辑:open--verfiy--restorevalue--transfervalue--close。

restorevalue和transfervalue之间做verify,transfervalue失败。

ExtBoardDevice调用方法

ExtBoardDevice extBoardDevice = (ExtBoardDevice) POSTerminal.getInstance(context).getDevice(POSTerminal.DEVICE_NAME_EXT_BOARD);

extBoardDevice.open();

extBoardDevice.close();

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

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

MDB factor decimal问题

一般factor设置1, decimal设置2. decimal设置成2的含义:因为mdb不能传输小数位数,这个设置可以让mdb传输带两个小数位数的数值,比如售货机设置0.01, 那么在协议命令里,可以设置值为1, 这样因为decimal为2, 所以实际的值是0.01. 有些售货机默认都是2, 不接受我们协议去更改。所以我们这边比如测试OPC的时候就设置为2. 售货机改不改都没关系。如果售货机会有其他值,那么再根据实际情况调整。

普通应用截屏接口

cloudpossdkV1.7.6.7以上

WizarViewAgentAssistant-v2.15.183+

com.cloudpos.advance.ext.system

ISystemDevice iSystemDevice = POSTerminalAdvance.getInstance().getSystemDevice();

iSystemDevice.open(context);

iSystemDevice .screenshot();

iSystemDevice .getScreenshotInPNG();

iSystemDevice.close();

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

wifi hotspot的设置API

cloudpossdkV1.7.6.7以上

WizarViewAgentAssistant-v2.15.179+

com.cloudpos.advance.ext.system.network

IWifiDevice iWifiDevice = POSTerminalAdvance.getInstance().getSystemDevice().getNetworkManager().getWifiManager();

iWifiDevice.open(context);

iWifiDevice.startWifiAp("wifitest","abcdefgh","WPA2_PSK",false);

iWifiDevice.stopWifiAp();

iWifiDevice.close();

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

检查终端是否root的接口

cloudpossdkV1.7.6.7及以上

WizarViewAgentAssistant-v2.15.164+

com.cloudpos.advance.ext.system

ISystemDevice iSystemDevice = POSTerminalAdvance.getInstance().getSystemDevice();

iSystemDevice.open(context);

iSystemDevice .isTerminalRooted();

iSystemDevice.close();

添加wifi证书,IUserCredentials使用

cloudpossdkV1.7.6.10 以上开始支持

WizarViewAgentAssistant-2.15.183以上开始

com.cloudpos.advance.ext.system.security

IUserCredentials iUserCredentials = POSTerminalAdvance.getInstance().getSystemDevice().getUserCredentialsManager();

iUserCredentials.open(context);

iUserCredentials.installCaCert(CaCertBuffer);

iUserCredentials.existCaCert(CaCertAlias);

iUserCredentials.getCaCert(CaCertAlias);

iUserCredentials.deleteCaCert(CaCertAlias);

iUserCredentials.installKeyPair(KeyPairAlias,type,privateKeyBuffer,certBuffer);

iUserCredentials.existKeyPair(KeyPairAlias);

iUserCredentials.removeKeyPair(KeyPairAlias);

iUserCredentials.clearCredentials();

iUserCredentials.close();

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

功能键唤醒终端(waking up terminal by function key )

cloudpossdkV1.7.7.3以上

WizarViewAgentAssistant-v2.15.186+

com.cloudpos.advance.ext.system

ISystemDevice iSystemDevice = POSTerminalAdvance.getInstance().getSystemDevice();

iSystemDevice.open(context);

iSystemDevice . setButtonWakeup (true);

iSystemDevice . isButtonWakeup ();

iSystemDevice.close();

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

普通应用kill支付应用接口

/** * Force-stops a payment application.

* *

This API terminates the target process immediately and clears its running state. * Only built-in/system payment applications are allowed to be terminated by this method. * Calling this on non-built-in apps will return {@code false} without performing any action.

* *

@param pkgName the package name of the payment application * @return {@code true} if the app process was successfully terminated; * {@code false} otherwise * * @requiresPermission android.permission.CLOUDPOS_APP_MANAGER
  • /

boolean stopApp(String pkgName);

cloudpossdkV1.7.7.18_Standard.aar及以上

agentAssistant 2.15.190及以上

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

ISystemDevice iSystemDevice = POSTerminalAdvance.getInstance().getSystemDevice();

iSystemDevice.open(context);

iSystemDevice.stopApp("com.example.paytest");

iSystemDevice.close();

获取打印以及卡片最后使用时间的应用接口

/** * Gets the last time the specified device was accessed.

  • * @param deviceName the unique name of the device *
@return the last used time in milliseconds since epoch, * or -1 if the device has never been used * see : System.currentTimeMillis() * 

@throws DeviceException for standard reasons as documented in {@link DeviceException NO_IMPLEMENT}.

  • /

abstract public long getDeviceLastAccessTime(String deviceName) throws DeviceException;

cloudpossdkV1.7.7.18_Standard.aar 验证ok

需配合agent 5.4.43+使用

system也有更新,解决RF时间不准确问题(例如Q3MU system1042+)

long result = POSTerminal.getInstance(context).getDeviceLastAccessTime(deviceName);

Admin/user登录/退出接口

cloudpossdkV1.7.7.18_Standard.aar及以上

需配合 possetting 1.32.208及以上

添加权限

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

com.cloudpos.advance.ext.system.login

ILoginDevice iLoginDevice = POSTerminalAdvance.getInstance().getSystemDevice().getLoginManager();

iLoginDevice.open(context);