# 系统要求
变更日志
版本 | 日期 | 变更 |
---|---|---|
v0.1 | 20180703 | 创建文档 |
v0.2 | 20180703 | 增加了证书交付,明确安装APP后无需打开APP |
v0.3 | 20180704 | 不在白名单的APP处理方式,增加厂商型号代码 |
v0.4 | 20180704 | 厂商序列号、随机数增加关于重复性的要求 |
v0.5 | 20180704 | 重复性要求描述改进 |
v0.6 | 20180709 | 增加Linux系统,增加厂商和机型 |
v0.7 | 20180717 | 1. 默认静默打开,额外增加不静默打开配置 2. 额外增加静默安装异常的日志记录 3. 增加序列号原则,获取方式 |
v0.8 | 20180726 | 增加厂商和机具 |
v0.9 | 20180726 | 增加厂商和机具 |
v1.0 | 20180816 | 增加本文档适用于微信支付订制机具的描述 |
v1.1 | 20180816 | 增加厂商和机具 |
v1.2 | 20180817 | 增加厂商和机具 |
v1.3 | 20180821 | 增加内置APP需求 |
v1.4 | 20180822 | 增加系统要求 |
v1.5 | 20211022 | 1.0版本发布 |
v1.6 | 20211112 | Windows打开安全保护指引 |
v1.7 | 20220401 | 排版更新 |
# 1. 基础要求
Android | Windows | |
系统版本 | Android 7.1以上,平板版本 | Windows 7 SP1、Windows 10 (版本20H2) 以上,支持32位和64位系统 |
系统权限 | - | WxpayFaceService运行需管理员权限 |
APP要求 | 预装刷脸SDK、IoT Service (重置系统需保留) |
# 2. 序列号要求 (All)
设备序列号,用于唯一标识一台设备。生成设备序列号时, 需要确保全局唯一性。
需要满足以下四个要求:
① 序列号在应用里面可以获取
② 序列号重装系统后不会变更
③ 序列号更换主板后可以写入
④ 序列号永远和机器后面铭牌上面贴的编号一致
注: 设备商必须保证设备SN符合微信制定要求, 否则由于SN规则不符导致的设备使用, 数据统计等问题, 设备商自行负责解决。
# 2.1. Android 构成规则:
设备商代码(2位) + 系统类型(1位) + 型号代码(2位) + 厂商序列号(10位) + 随机数(8位)
字段名 | 长度 | 内容/格式 | 字符取值范围 |
---|---|---|---|
设备商代码 | 2 | 由微信支付为厂商分配 | 0-9, A-Z |
系统类型 | 1 | A:表示Android,W:表示Windows,L:表示Linux | 0-9, A-Z |
型号代码 | 2 | 由厂商提供设备型号给微信支付, 微信支付分配型号代码给厂商 | 0-9, A-Z |
厂商序列号 | 10 | 格式内容由厂商按自家现有规则来确定。 | 0-9, A-Z |
随机数 | 8 | 随机数每一位在0-9, A-Z中取值。需要保证随机性,对随机数的唯一性可以不作强校验。 (注意随机数生成时,先初始化) | 0-9, A-Z |
微信读取序列号方式:
Android 8 开始声明, Build.SERIAL获取SN方式将被废弃,将使用Build.getSerial()。 Android 9 已经正式废弃原方式
# 2.2. Windows 构成规则:
字段名 | 长度 | 内容/格式 | 字符取值范围 |
---|---|---|---|
设备商代码 | 2 | 由微信支付为厂商分配 | 0-9, A-Z |
系统类型 | 1 | A: 表示Android, W: 表示Windows, L表示Linux | 0-9, A-Z |
型号代码 | 2 | 由厂商提供设备型号给微信支付, 微信支付分配型号代码给厂商 | 0-9, A-Z |
厂商序列号 | 12 | 使用网卡MAC地址 | 0-9, A-Z |
随机数 | 6 | 随机数每一位在0-9, A-Z中取值。需要保证随机性,对随机数的唯一性可以不作强校验。 (注意随机数生成时,先初始化) | 0-9, A-Z |
微信读取序列号方式:
- 1.27版本以上推荐方式:
SN写入BIOS设置,参考MSDN上WMI与CIM文档,对应wmic命令 wmic bios get SerialNumber。
提示:如果主板使用DMIEDIT,SN对应项是[Type 001] -- System Information项的Serial Number字段。 - 旧方式:
从c:\serial.txt中读取。 在安装人脸SDK之前,需要先生成这个文件。
注意:在使用1.27版本以上SDK,同时使用BIOS及txt文本设置设备SN,优先读取BIOS。
# 3. 软件预检测项适配指引
本页主要针对微信刷脸支付-软件预检测中的检测项提供适配说明,软件预检测项会在人脸设备出厂检查环节进行验收和拦截。
# 3.1 Android 相关
# 3.1.1 系统语言、日期和时间
要求:系统语言默认为中文,【设置】-【时间】开启“自动确定日期和时间”和“自动确定时区”。
效果:
实现参考:修改build/make/target/product/full_base.mk,将PRODUCT_LOCALES的值修改为zh_CN
PRODUCT_LOCALES := zh_CN
修改frameworks\base\packages\SettingsProvider\res\values\defaults.xml
<bool name="def_auto_time">true</bool>
<bool name="def_auto_time_zone">true</bool>
# 3.1.2 预置人脸App和IoTService App
要求:微信支付相关App(如人脸App、IoTService App等,请参考附录:微信支付相关App)需要提前预置到/system/priv-app目录。
人脸App预置方案如下:
1、获取最新版本人脸App,将其重命名为wxpayface.apk;
2、新建wxpayface目录,把wxpayface.apk放进该目录(注意目录名和apk名称需保持一致);
3、用zip工具打开wxpayface.apk,把里边的lib拉出来放在wxpayface目录下,把lib目录下的armeabi-v7a目录改为arm;
4、将wxpayface目录所有内容预置到/system/priv-app/目录下;
5、最终目录内容如下:
/system/priv-app/wxpayface目录下有一个wxpayface.apk
/system/priv-app/wxpayface/lib/arm目录下有wxpayface自带的so库
IoTService App预置方案如下:
1、获取最新版本IoTService App,将其重命名为iotservice.apk(也可以修改为其他名称,但需要注意apk所在目录名和apk文件名需保持一致);
2、新建iotservice目录,把iotservice.apk放进该目录(注意目录名和apk名称需保持一致);
3、用zip工具打开iotservice.apk,把里边的lib拉出来放在iotservice目录下,把lib目录下的armeabi-v7a目录改为arm;
4、将iotservice目录所有内容预置到/system/priv-app/目录下;
5、最终目录内容如下:
/system/priv-app/iotservice目录下有一个iotservice.apk
/system/priv-app/iotservice/lib/arm目录下有iotservice自带的so库
实现参考:
以下是支付侧提供的一份在源码目录通过编译脚本预置人脸App的样例代码(IoTService类似),可供参考:
1、 把人脸App修改为wxpayface.apk;
2、 在Rom源码的适当位置创建一个目录,该目录包含三个文件,分别是apk文件wxpayface.apk,解压脚本update_so.sh,编译文件Android.mk;
3、 编译Rom源码,查看输出目录是否能产生/system/priv-app/wxpayface目录,apk及相应so是否都正确解压。
update_so.sh内容如下:
#!/bin/bash
rm -rf $1/lib
mkdir -p $1/lib
rm -rf $1/apkUnzip
mkdir -p $1/apkUnzip
unzip -o $1/$2.apk -d $1/apkUnzip
copy_abi_folder() {
for fileName in $(ls $1/apkUnzip/lib); do
if [[ -d $1/apkUnzip/lib/$fileName ]]; then
if [ $fileName == 'arm64-v8a' ]; then
cp -rf $1/apkUnzip/lib/$fileName $1/lib/arm64
rm -rf $1/apkUnzip
return 0
fi
fi
done
for fileName in $(ls $1/apkUnzip/lib); do
if [[ -d $1/apkUnzip/lib/$fileName ]]; then
if [ $fileName == 'armeabi-v7a' ]; then
cp -rf $1/apkUnzip/lib/$fileName $1/lib/arm
rm -rf $1/apkUnzip
return 0
fi
fi
done
for fileName in $(ls $1/apkUnzip/lib); do
if [[ -d $1/apkUnzip/lib/$fileName ]]; then
if [ $fileName == 'arm' ]; then
cp -rf $1/apkUnzip/lib/$fileName $1/lib
rm -rf $1/apkUnzip
return 0
fi
fi
done
cp -rf $1/apkUnzip/lib $1
rm -rf $1/apkUnzip
return 0
}
copy_abi_folder $1
Android.mk内容如下:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
###人脸App: wxpayface, IoTService App: iotservice
LOCAL_MODULE := wxpayface
LOCAL_MODULE_CLASS := APPS
LOCAL_MODULE_TAGS := optional
LOCAL_BUILT_MODULE_STEM := package.apk
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
LOCAL_CERTIFICATE := PRESIGNED
LOCAL_PRIVILEGED_MODULE := true
LOCAL_MULTILIB := 32
LOCAL_SRC_FILES := $(LOCAL_MODULE).apk
LOCAL_DEX_PREOPT := false
###解压缩获取so
$(shell `$(LOCAL_PATH)/update_so.sh $(LOCAL_PATH) $(LOCAL_MODULE)`)
###清空临时变量JNI_LIBS
JNI_LIBS :=
###当前目录递归搜索
$(foreach FILE,$(shell find $(LOCAL_PATH)/lib/ -name *.so), $(eval JNI_LIBS += $(FILE)))
###获取搜索文件目录集(相对目录)
LOCAL_PREBUILT_JNI_LIBS := $(subst $(LOCAL_PATH),,$(JNI_LIBS))
include $(BUILD_PREBUILT)
# 3.1.3 默认授予人脸App和IoTService App应用权限
要求:微信支付相关App(如人脸App、IoTService App等,请参考文末附录:微信支付相关App)需要提前授予应用权限。
实现参考:
以下是支付侧提供的通过修改framework代码实现静默授权,可供参考:
Android 9以下版本:
修改framework/base/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java,增加动态权限授予:
private void grantDefaultSystemHandlerPermissions(int userId) {
//这里给我们应用权限和添加白名单
//如果是人脸,包名修改为:com.tencent.wxpayface
//如果是IoTService,包名修改为:com.tencent.wxpayface.iotservice
//其他包名参考附录
PackageParser.Package wxpayfacePackage = getSystemPackageLPr("com.tencent.wxpayface");
if (wxpayfacePackage != null && doesPackageSupportRuntimePerissions(wxpayfacePackage)) {
if (DEBUG) {
Log.d(TAG, "grantRuntimePermissionsLPw com.tencent.wxpayface");
}
grantRuntimePermissionsLPw(wxpayfacePackage, PHONE_PERMISSIONS, userId);
grantRuntimePermissionsLPw(wxpayfacePackage, STORAGE_PERMISSIONS, userId);
grantRuntimePermissionsLPw(wxpayfacePackage, LOCATION_PERMISSIONS, userId);
grantRuntimePermissionsLPw(wxpayfacePackage, CAMERA_PERMISSIONS, userId);
grantRuntimePermissionsLPw(wxpayfacePackage, SENSORS_PERMISSIONS, userId);
}
PackageParser.Package iotPackage = getSystemPackageLPr("com.tencent.wxpayface.iotservice");
if (iotPackage != null && doesPackageSupportRuntimePerissions(iotPackage)) {
if (DEBUG) {
Log.d(TAG, "grantRuntimePermissionsLPw com.tencent.wxpayface.iotservice");
}
grantRuntimePermissionsLPw(iotPackage, PHONE_PERMISSIONS, userId);
grantRuntimePermissionsLPw(iotPackage, STORAGE_PERMISSIONS, userId);
grantRuntimePermissionsLPw(iotPackage, LOCATION_PERMISSIONS, userId);
grantRuntimePermissionsLPw(iotPackage, CAMERA_PERMISSIONS, userId);
grantRuntimePermissionsLPw(iotPackage, SENSORS_PERMISSIONS, userId);
}
}
Android 10以上版本:
修改framework/base/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java,增加动态权限授予:
public void grantDefaultPermissions(int userId) {
//这里给我们应用权限和添加白名单
//如果是人脸,包名修改为:com.tencent.wxpayface
//如果是IoTService,包名修改为:com.tencent.wxpayface.iotservice
try {
//com.tencent.wxpayface
PackageInfo pkg = mContext.getPackageManager().getPackageInfo("com.tencent.wxpayface", DEFAULT_PACKAGE_INFO_QUERY_FLAGS);
grantRuntimePermissionsForSystemPackage(userId, pkg);
} catch (Throwable e) {
Log.e(TAG, "grantRuntimePermissionsForSystemPackage com.tencent.wxpayface err:", e);
}
try {
//com.tencent.wxpayface.iotservice
PackageInfo pkg = mContext.getPackageManager().getPackageInfo("com.tencent.wxpayface.iotservice", DEFAULT_PACKAGE_INFO_QUERY_FLAGS);
grantRuntimePermissionsForSystemPackage(userId, pkg);
} catch (Throwable e) {
Log.e(TAG, "grantRuntimePermissionsForSystemPackage com.tencent.wxpayface.iotservice err:", e);
}
// 这里结束
grantPermissionsToSysComponentsAndPrivApps(userId);
grantDefaultSystemHandlerPermissions(userId);
grantDefaultPermissionExceptions(userId);
synchronized (mLock) {
mDefaultPermissionsGrantedUsers.put(userId, userId);
}
}
# 3.1.4 实现人脸App和IoTService App静默升级
要求:微信支付相关App(如人脸App、IoTService App等,请参考文末附录:微信支付相关App)进行升级时,不显示升级界面,直接进行升级。
实现参考:
以下是人脸App升级调用逻辑(其他App升级调用逻辑类似),可创建一个Demo App,通过以下代码调用人脸软件升级,查看是否可以静默升级:
public class APKUtils {
private final static String TAG = "APKUtils";
public static final int FLAG_PRIVILEGED = 1<<30;
public static int getVersionCode(Context context, String str) {
try {
return context.getPackageManager().getPackageInfo(str, 0).versionCode;
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
public static String getVersionName(Context context, String str) {
try {
return context.getPackageManager().getPackageInfo(str, 0).versionName;
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
public static void installApk(Context context, String apkFileString) {
File apkFile = new File(apkFileString);
if (!apkFile.exists()) {
Log.d(TAG, "apk file is not exist: " + apkFileString);
Toast.makeText(context, "文件不存在", Toast.LENGTH_SHORT).show();
return;
}
Log.d(TAG, "use normal install");
Intent intent = new Intent(Intent.ACTION_VIEW);
//AndroidN以上版本需通过FileProvider进行升级
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
Log.d(TAG, "use provider url");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Uri contentUri = FileProvider.getUriForFile(context, context.getPackageName() + ".fileProvider", apkFile);
intent.setDataAndType(contentUri, "application/vnd.android.package-archive");
List<ResolveInfo> resInfoList = context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo resolveInfo : resInfoList) {
String packageName = resolveInfo.activityInfo.packageName;
context.grantUriPermission(packageName, contentUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
} else {
Log.d(TAG, "use path url");
intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
context.startActivity(intent);
Log.d(TAG, "use normal install end");
}
}
静默升级异常处理:
对于安装失败的情况,需要把安装异常记录在指定位置的文件里。
异常日志目录
Environment.getExternalStorageDirectory().getAbsolutePath() + "/wxpayface/install_log/"
异常日志文件名
包名.log
例如
com.tencent.wxpayface.log
异常日志记录格式
- 只需要一行,每次覆盖
- 数据格式:
时间戳,包名,当前版本,新安装版本,错误码
例如
1531821994,com.tencent.wxpayface,1.0.9,1.1.0,-18
# 3.1.5 关闭USB授权弹框
要求:人脸App会开启3D摄像头,在开启时不会弹出USB授权框。
修改/frameworks/base/core/res/res/values/config.xml
文件,将config_disableUsbPermissionDialogs
修改为true,如下所示:
<!-- If true, then we do not ask user for permission for apps to connect to USB devices.
Do not set this to true for production devices. Doing so will cause you to fail CTS. -->
<bool name="config_disableUsbPermissionDialogs">true</bool>
# 3.1.6 开启系统定位
要求:【设置】-【位置信息】默认开启,模式为高精度,同时开启WLAN扫描、蓝牙扫描。
备注:需同时默认授予人脸App、IoTService App定位权限,详情见"默认授予人脸App和IoTService App应用权限"。
效果:
实现参考:修改frameworks\base\packages\SettingsProvider\res\values\defaults.xml
<string name="def_location_providers_allowed" translatable="false">gps,network</string>
<integer name="def_wifi_scan_always_available">1</integer>
# 3.1.7 导航栏
要求:【设置】-【显示】增设“导航栏”开关。
功能说明:
1、“导航栏”开关控制系统是否有导航栏,默认值:关闭。
2、开启时: 表示系统有导航栏,但默认隐藏,底部上滑可以呼出,10秒后自动隐藏。
3、关闭时: 表示系统没有导航栏,底部上滑不能呼出。
4、备注:如不实现此设置,需保证导航栏默认隐藏,且上滑不能呼出。
# 3.1.8 微信支付相关App
App | 包名 | 功能说明 | 使用场景 | 是否需要预置 | 是否需要静默授权 | 是否需要静默升级 |
---|---|---|---|---|---|---|
人脸App/SDK | com.tencent.wxpayface | 刷脸支付 | 青蛙设备、SDK设备 | 是 | 是 | 是 |
IoTService | com.tencent.wxpayface.iotservice | IoT设备管理 | 青蛙设备、SDK设备 | 是 | 是 | 是 |
# 3.1.9 常见出厂检查拦截提示与适配指引
检查失败提示语 | 适配指引 |
---|---|
人脸APP/人脸SDK没有预置进系统 | 见适配指引-预置人脸App和IoTService App |
IoTService没有预置进系统 | 见适配指引-预置人脸App和IoTService App |
# 3.2 Windows 相关
# 3.2.1 系统开启UAC
(1)方式一
- 设备系统需在系统设置里开启UAC
- 设置路径:控制面板->用户账户->用户账户->更改用户账户控制设置
- 将UAC等级调为默认或者始终通知
详细设置步骤如下图所示:
(2)方式二
使用注册表导入
首先,请点击进入注册表;
其次,双击文件->确认导入,重启系统即可。