在S5PV210/Exynos4412平台上实现二代身份证解码,安卓系统,可以获取全部文字信息以及电子照片
二代居名身份证阅读器是公安部第一研究所证件技术事业部利用国际上先进的TypeB非接触IC卡阅读技术,通过内嵌的专用身份证安全控制模块(SAM),以无线传输方式与第二代居民身份证内的专用芯片进行安全认证后,将芯片内的个人信息资料读出,再通过各种通讯接口,将此信息上传至各种处理终端。由处理终端将这些信息解码成文字和相片进行显示或者存储。 首先、你得拥有公安部指定厂家生产的专用身份证安全控制模块(SAM)以及与之配套的CPU板以及天线,有了以上硬件,才能进行读卡操作,本实验平台是基于九鼎创展科技的X210V3/x4412开发板,开发平台如图所示:
有了以上准备工作后,我们就可以进行具体的开发工作了,首先我们需要定义一个JNI接口,包装与身份证阅读模块间的各种通信指令,包括找卡、选卡、读卡等等,主要参考资料中国标准出版社出版的GA标准 “GA 467-2013 居民身份证验证安全控制模块接口技术规范” - public class idcard
- {
- static
- {
- System.loadLibrary("idcard");
- }
-
- public final static native String version();
- public final static native int open(String path, int baud);
- public final static native boolean close(int fd);
-
- public final static native boolean reset(int fd);
- public final static native boolean check(int fd);
- public final static native byte[] readSerialNumber(int fd);
- public final static native byte[] readManageAccount(int fd);
-
- public final static native boolean findCard(int fd);
- public final static native boolean selectCard(int fd);
- public final static native byte[] readCard(int fd);
- public final static native byte[] readCardAppend(int fd);
- }
复制代码 译码类,主要将读到的字节信息,文字已解码,图片未解码,转为可读信息,用于各种控件的显示:- package org.xboot.idcard;
- import java.io.FileInputStream;
- import java.io.UnsupportedEncodingException;
- import android.graphics.Bitmap;
- import android.graphics.BitmapFactory;
- import com.ivsign.android.IDCReader.IDCReaderSDK;
- public class decode
- {
- private byte[] buffer;
- private String temp;
-
- public decode(byte[] buf)
- {
- buffer = buf;
-
- try {
- String t = new String(buf, "UTF16-LE");
- temp = new String(t.getBytes("UTF-8"));
- } catch (UnsupportedEncodingException e) {
- e.printStackTrace();
- }
- }
-
- public final String name() {
- int start = (4 + 0) / 2;
- int end = (4 + 30) / 2;
- return temp.substring(start, end);
- }
-
- public final String sex() {
- int start = (4 + 30) / 2;
- int end = (4 + 32) / 2;
- String s = temp.substring(start, end);
- if(s.equals("0"))
- return "未知";
- else if(s.equals("1"))
- return "男";
- else if(s.equals("2"))
- return "女";
- return "未知";
- }
-
- public final String nation()
- {
- int start = (4 + 32) / 2;
- int end = (4 + 36) / 2;
- String s = temp.substring(start, end);
- int code = Integer.parseInt(s.toString());
- switch(code)
- {
- case 1:
- return "汉";
- case 2:
- return "蒙古";
- case 3:
- return "回";
- case 4:
- return "藏";
- case 5:
- return "维吾尔";
- case 6:
- return "苗";
- case 7:
- return "彝";
- case 8:
- return "壮";
- case 9:
- return "布依";
- case 10:
- return "朝鲜";
- case 11:
- return "满";
- case 12:
- return "侗";
- case 13:
- return "瑶";
- case 14:
- return "白";
- case 15:
- return "土家";
- case 16:
- return "哈尼";
- case 17:
- return "哈萨克";
- case 18:
- return "傣";
- case 19:
- return "黎";
- case 20:
- return "傈僳";
- case 21:
- return "佤";
- case 22:
- return "畲";
- case 23:
- return "高山";
- case 24:
- return "拉祜";
- case 25:
- return "水";
- case 26:
- return "东乡";
- case 27:
- return "纳西";
- case 28:
- return "景颇";
- case 29:
- return "柯尔克孜";
- case 30:
- return "土";
- case 31:
- return "达斡尔";
- case 32:
- return "仫佬";
- case 33:
- return "羌";
- case 34:
- return "布朗";
- case 35:
- return "撒拉";
- case 36:
- return "毛南";
- case 37:
- return "仡佬";
- case 38:
- return "锡伯";
- case 39:
- return "阿昌";
- case 40:
- return "普米";
- case 41:
- return "塔吉克";
- case 42:
- return "怒";
- case 43:
- return "乌孜别克";
- case 44:
- return "俄罗斯";
- case 45:
- return "鄂温克";
- case 46:
- return "德昂";
- case 47:
- return "保安";
- case 48:
- return "裕固";
- case 49:
- return "京";
- case 50:
- return "塔塔尔";
- case 51:
- return "独龙";
- case 52:
- return "鄂伦春";
- case 53:
- return "赫哲";
- case 54:
- return "门巴";
- case 55:
- return "珞巴";
- case 56:
- return "基诺";
- case 97:
- return "其他";
- case 98:
- return "外国血统中国籍人士";
- default:
- break;
- }
- return "";
- }
-
- public final String birth()
- {
- int start = (4 + 36) / 2;
- int end = (4 + 52) / 2;
- return temp.substring(start, end);
- }
-
- public final String address()
- {
- int start = (4 + 52) / 2;
- int end = (4 + 122) / 2;
- return temp.substring(start, end);
- }
-
- public final String no()
- {
- int start = (4 + 122) / 2;
- int end = (4 + 158) / 2;
- return temp.substring(start, end);
- }
-
- public final String police()
- {
- int start = (4 + 158) / 2;
- int end = (4 + 188) / 2;
- return temp.substring(start, end);
- }
- public final String from()
- {
- int start = (4 + 188) / 2;
- int end = (4 + 204) / 2;
- return temp.substring(start, end);
- }
-
- public final String to()
- {
- int start = (4 + 204) / 2;
- int end = (4 + 220) / 2;
- return temp.substring(start, end);
- }
- }
复制代码 有了这两个基本类,我们就可以实现对身份的阅读操作了,主要参考代码如下: 首先我们需要将打开通信端口,并获取其句柄,用于后面的各种操作 - public void start() {
- fd = idcard.open("/dev/ttySAC0", 115200);
- handler.postDelayed(runnable, 200);
-
- startBtn.setEnabled(false);
- stopBtn.setEnabled(true);
- }
复制代码 同样,我们在结束应用软件时,需要关闭通信端口 - public void stop() {
- idcard.close(fd);
- handler.removeCallbacks(runnable);
- startBtn.setEnabled(true);
- stopBtn.setEnabled(false);
- }
复制代码 在准备好后,我们就可以创建一个线程,用于等待刷卡事件,并做相应的处理,代码如下: - handler = new Handler();
- runnable = new Runnable() {
- @Override
- public void run() {
- handler.postDelayed(this, 50);
- try {
- Thread.sleep(20);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- if (!idcard.findCard(fd))
- return;
- try {
- Thread.sleep(10);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- if (!idcard.selectCard(fd))
- return;
- try {
- Thread.sleep(10);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- byte[] result = idcard.readCard(fd);
- if (result == null)
- return;
- decode d = new decode(result);
- msgTv.setText(
- "姓名:" + d.name() + "\n" +
- "性别:" + d.sex() + "\n" +
- "民族:" + d.nation() + "\n" +
- "出生:" + d.birth() + "\n" +
- "住址:" + d.address() + "\n" +
- "身份证号:" + d.no() + "\n" +
- "签发机关:" + d.police() + "\n" +
- "有效期限:" + d.from() + "-" + d.to() + "\n");
-
- playSound();
-
- Bitmap bmp = d.bmp();
- if(bmp != null)
- photoImg.setImageBitmap(bmp);
- else
- photoImg.setImageDrawable(getResources().getDrawable(R.drawable.face));
- }
- };
复制代码 从代码中可以看出,先找卡,找到后执行选卡指令,成功选卡后,就可以进行读卡了,读卡信息保存在result数组里,我们需要用上面的decode进行译码转成可读格式。当然在具体的代码实现中,成功读卡后,播放了一小段音乐,用于提示成功刷卡,最后是解码电子照片,并显示在ImageView控件上。
软件启动后界面如下: 点击开始按钮,创建读卡线程,我们放入一张身份证,就可以读卡此身份证的相关信息,如果所示:
已经测试完成的读卡应用程序:
IdCard.apk
(330.86 KB, 下载次数: 29)
|