历数OpenMobile开发的一些坑

1 篇文章 0 订阅
一、开发时配置应用程序使用手机系统自带的OpenMObile API库
因为OpenMobile不能向下兼容,各版本支持手机系统版本不同:
15 - API level 15, Android 4.0.3
16 - not available
17 - API level 17, Android 4.2
18 - API level 18, Android 4.3
19 - API level 19, Android 4.4
20 - developer preview, not available
21 - API level 21, Android 5.0

所以在开发时不能使用其中任一版本,使用任一版本将可能导致部分支持NFC手机无法使用应用,出现兼容性问题;
推荐做法是:通过配置使用手机系统自带OpenMobile库
配置流程为:
(1)准备某一版本OpenMobile API支撑库,导入项目libs,该支撑库只在编译期间使用,项目打包不将此包打入到项目里,

     配置项为:(工具Android studio 中app->build.gradle中配置)

dependencies {
    compile fileTree(include: ['*.jar'], exclude: ['openMobileApi.jar'], dir: 'libs')
    compile 'com.android.support:appcompat-v7:23.4.0'
    compile 'com.google.code.gson:gson:2.8.0'
    compile 'com.squareup.okhttp3:okhttp:3.5.0'
    //provided表示只在编译时使用jar包,运行时默认环境中已经存在这个jar包了
    //jar包在网上有下载,注意要与ROM版本一直,不要一味求最新
    provided files('libs/openMobileApi.jar')
}



(2)工程主配置文件AndroidManifest.xml配置使用系统自带OpenMobile库:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  package="com.cosw">


  <uses-permission android:name="android.permission.INTERNET" />
  <uses-permission android:name="android.permission.NFC" />
  <uses-permission android:name="org.simalliance.openmobileapi.SMARTCARD" />
  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />


  <uses-feature android:name="android.hardware.nfc" />


  <application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:name="com.cosw.app.MyApplication"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">


	 <!--true:使用系统自带OpenMobile库-->
    <uses-library
      android:name="org.simalliance.openmobileapi"
      android:required="true" />


    <activity
      android:name="com.cosw.app.SplashActivity"
      android:screenOrientation="portrait">
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
    </activity>
  </application>
</manifest>


以上配置之后,即可实现编译期使用libs库,打包时使用系统自带库的目标。


二、使用OpenMObile API获取SWP卡ATR进行密钥分散时注意点

1、手机必须支持NFC
2、卡片放到手机里之后,手机会获取卡片ATR,并且保存到手机,此时手机是肯定拿到了ATR的,在此基础上通过openmobile API的session.getATR()可能存在以下两种情况
   (1)使用openmobile API的session.getATR()获取ATR时,部分手机获取为null,也有的手机获取atr为全0(OPPO N5207支持NFC,但是ATR获取为全0000000000)
   (2)同一张支持NFC的SWP卡,不同的手机获取的ATR不同,区别在于多了个TCK校验位(如果想忽略校验位,可以通过ATRParseUtil判断是否存在校验位,存在则去掉校验位。)
        ATR格式说明参考:
        说明文档:https://en.wikipedia.org/wiki/Answer_to_reset
        ATR解析工具:http://ruimtools.com/atr.php
3、ATR:可以作为分散密钥的因子,主要用的是历史字节数进行分散。
4、通过手机自带的openmobile API去获取atr,同一张卡片可能获取的atr不同,不同之处在于ATR的校验位,该校验位有可能存在,也与可能不存在
示例:
ATR(TCK存在,需要校验,此时有的手机能拿到20校验位I,有的手机不行)
   (和包UAT测试卡1)华为p10       ------atr=3B9F95803FC7A08031E073FE211B633A104E83009000
   (和包UAT测试卡1)华为 honor    ------atr=3B9F95803FC7A08031E073FE211B633A104E8300900020
   (和包UAT测试卡1)中国移动M812C ------atr=3B9F95803FC7A08031E073FE211B633A104E8300900020

   (蓝牙卡)atr:3B9194803FC3A0BC65

ATR(TCK不存在,不需要校验):3B9A96401E4100014308025054E20D

5、如果存在需要判断ATR是否存在校验位,存在则去掉校验位的需求,则可以参考下面代码:

package com.atr.test;

/**
 * 
 * @author Administrator ATR判断是否存在检验字节 参考:<br/>
 *         (1)http://www.ruimtools.com/atr.php<br/>
 *         (2)https://en.wikipedia.org/wiki/Answer_to_reset<br/>
 *         ATR(TCK存在,需要校验,此时有的手机能拿到20校验位I,有的手机不行)<br/>
 *         华为p10 ------atr=3B9F95803FC7A08031E073FE211B633A104E83009000<br/>
 *         华为 honor ------atr=3B9F95803FC7A08031E073FE211B633A104E83009000 20<br/>
 *         中国移动M812C ------atr=3B9F95803FC7A08031E073FE211B633A104E83009000 20
 *         atr:3B9194803FC3A0BC65<br/>
 *         ATR(TCK不存在,不需要校验):3B9A96401E4100014308025054E20D<br/>
 * 
 */
public class ATRParseUtil {

	public static void main(String[] args) {
		/*System.out.println(parseATR(StringUtil
				.hexStringToByteArray("3B9194803FC3A0BC65")));*/
		
		try {
			int atrZero=Integer.valueOf("0000000000");
		} catch (Exception e) {
			System.out.println("不为全0");
		}
		System.out.println("全0");
	}

	// 历史字节数
	private static int historical_len = 0;
	// 已经校验的字节数
	private static int info_recv_len = 1;

	public static String parseATR(byte[] atr) {

		//(1)部分支持NFC机型获取ATR为null,可能底层没有缓存卡片ATR
		if (atr == null || atr.length == 0) {
			return null;
		}
		
		//(2)ATR为全0也无法做个人化,代表机型:
		//(1)OPPO N5207
		try {
			int atrZero=Integer.valueOf(StringUtil.byteArrayToHexString(atr));
			if(atrZero==0){
				System.out.println("获取ATR为全0,不支持");
                return null;
			}
		} catch (Exception e) {
		}
		
		int ta_b = 0;
		int tb_b = 0;
		int tc_b = 0;
		int td_b = 0;
		int tck_exist = 0;
		int ta1_exist = 0;

		// 每次计算之前从新初始化
		historical_len = 0;
		info_recv_len = 1;
		info_recv_len++;
		ta_b = (atr[info_recv_len - 1] >> 4) & 0x01;
		tb_b = (atr[info_recv_len - 1] >> 5) & 0x01;
		tc_b = (atr[info_recv_len - 1] >> 6) & 0x01;
		td_b = (atr[info_recv_len - 1] >> 7) & 0x01;
		// 格式字节T0 在范围[0..15] 中以其4个低位(第4个MSbit到第1个LSbit)编码历史字节T i的数量K
		// 示例1:3B9194803FC3A0BC65:91->10010001 ,此时历史字节数为1
		// 示例2:3B9F95803FC7A08031E073FE211B633A104E8300900020:9F->10011111,,此时历史字节数为15
		historical_len = atr[info_recv_len - 1] & 0x0F;
		System.out.println(atr[info_recv_len - 1]);

		if (ta_b != 0) {
			ta1_exist = 1;
		}

		boolean flag = isTCKExist(ta_b, tb_b, tc_b, td_b, ta1_exist, tck_exist,
				atr);
		System.out.println("是否存在TCK校验位:" + flag);
		System.out.println("info_recv_len:" + info_recv_len);
		System.out.println("historical_len:" + historical_len);
		if (flag) {// 存在TCK校验位
			byte[] atrCopy = new byte[historical_len + info_recv_len];
			System.arraycopy(atr, 0, atrCopy, 0, atrCopy.length);
			return StringUtil.byteArrayToHexString(atrCopy);
		} else {// 不存在TCK校验位
			return StringUtil.byteArrayToHexString(atr);
		}

	}

	/**
	 * 是否存在TCK校验位
	 * 
	 * @param ta_b
	 * @param tb_b
	 * @param tc_b
	 * @param td_b
	 * @param ta1_exist
	 * @param tck_exist
	 * @param atr
	 * @return
	 */
	private static boolean isTCKExist(int ta_b, int tb_b, int tc_b, int td_b,
			int ta1_exist, int tck_exist, byte atr[]) {
		if (ta_b != 0) {
			ta_b = 0;
			info_recv_len++;
			if (ta1_exist == 1) {
				ta1_exist = 0;
			}
			return isTCKExist(ta_b, tb_b, tc_b, td_b, ta1_exist, tck_exist, atr);
		} else if (tb_b != 0) {
			tb_b = 0;
			info_recv_len++;
			return isTCKExist(ta_b, tb_b, tc_b, td_b, ta1_exist, tck_exist, atr);
		} else if (tc_b != 0) {
			tc_b = 0;
			info_recv_len++;
			return isTCKExist(ta_b, tb_b, tc_b, td_b, ta1_exist, tck_exist, atr);
		} else if (td_b != 0) {
			td_b = 0;
			info_recv_len++;
			System.out.println("td_b:"
					+ StringUtil.byteToHexString(atr[info_recv_len - 1]));
			System.out.println("td_b:" + atr[info_recv_len - 1]);
			ta_b = (atr[info_recv_len - 1] >> 4) & 0x01;
			tb_b = (atr[info_recv_len - 1] >> 5) & 0x01;
			tc_b = (atr[info_recv_len - 1] >> 6) & 0x01;
			td_b = (atr[info_recv_len - 1] >> 7) & 0x01;
			System.out.println(atr[info_recv_len - 1] & 0x0F);
			if ((atr[info_recv_len - 1] & 0x0F) != 0x00)
				tck_exist = 1;
			return isTCKExist(ta_b, tb_b, tc_b, td_b, ta1_exist, tck_exist, atr);
		}
		return tck_exist != 0 ? true : false;
	}
}

参考链接:

https://github.com/seek-for-android/pool/wiki/UsingSmartCardAPI
https://stackoverflow.com/questions/32438584/open-mobile-api-with-android-21
http://www.jianshu.com/p/a3a3b3db6b37
NFC读取sim卡之建立卡连接:http://blog.csdn.net/qq_24224369/article/details/52371153
检查手机是否连接网络,sim的存在以及是否支持NFC功能:http://blog.csdn.net/qq_24224369/article/details/52693454
7816 ATR解析工具:http://www.ruimtools.com/atr.php
ATR应答重置:https://en.wikipedia.org/wiki/Answer_to_reset
SIMalliance OMAPI transport test plan介绍之二接口规范篇:http://www.51testing.com/html/36/489136-831212.html
openmobile api:http://seek-for-android.github.io/javadoc/V4.0.0/org/simalliance/openmobileapi/package-summary.html
sim卡与短信息:http://www.ithao123.cn/content-170384.html



  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
万年历数码管是一种数字显示器,用来显示年、月、日等日期信息。我们可以使用Verilog语言来实现它。首先,我们需要定义数码管的显示方式和逻辑控制。 在Verilog中,我们可以通过定义数码管的输入和输出端口来表示数码管的连接和控制。我们可以定义一个模块,该模块包括数码管的输入端口(如年、月、日的输入)和输出端口(数码管的显示输出),同时也需要包括逻辑控制部分。 逻辑控制部分可以根据输入的年、月、日等信息来计算具体的日期,然后将其转换为数码管能够显示的格式。这个转换过程可以通过使用逻辑门、时序逻辑以及状态机来实现。我们可以使用Verilog提供的各种逻辑门和时序逻辑的描述方式来编写相应的逻辑控制部分。 另外,我们还需要定义数码管的显示方式。可以通过使用Verilog实现七段数码管的显示方式,将输入的日期信息转换成对应的数码管的显示形式。同时,我们还需要考虑到数码管的刷新频率和显示的稳定性,以及是否需要考虑到灵活的数字切换功能等。这些特殊需求也可以通过Verilog语言来实现。 总而言之,通过Verilog语言的描述和实现,我们可以设计出一个完整的万年历数码管,并能够根据输入的日期信息进行正确的显示。Verilog的灵活性和强大的逻辑描述能力可以帮助我们实现这样的数字逻辑功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值