Android eSE

Android eSE 访问接口

NFC 基于eSE的卡模拟,需要访问eSE,来替换秘钥建立安全域下载Applet个人化应用读取个人信息等。这些过程都要通过OMA接口访问,OMA其实是一个装饰APK,来给上层提供统一的API。目前SE实现方式主要分三种 基于SIM卡的 基于SD卡的和基于内置eSE的。针对不同的实现有不同的终端,所以用OMA包装一层,现阶段基于eSE的方案,最终的实现在NFC系统APK中,这是一个系统级别的服务,开机自启动运行。NFC整体架构如下。

访问OMA接口前置条件

权限要求

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

这两个权限是OMA校验访问者需要的权限,如果没有申请,客户端调用OMA接口的时候会抛出异常。

签名要求

只申请权限仅仅是一方面,有eSE不是任何人想访问就可以访问的,所以一般有系统配置文件决定,那一类apk有访问权限,也就是所谓的白名单,这白名单是由自己掌控的。其实就是Access control Enforcer 这层。 雪球自己又加了一层校验。白名单放在 system/etc/sbtoma_access.xml
具体格式

1
2
3
4
5
6
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<signer android:signature="308201e53082014ea00302010202044f711c41300d06092a864886f70d01010505003037310b30090603550406130255533110300e060355040a1307416e64726f6964311630140603550403130d416e64726f6964204465627567301e170d3132303332373031343734355a170d3432303332303031343734355a3037310b30090603550406130255533110300e060355040a1307416e64726f6964311630140603550403130d416e64726f696420446562756730819f300d06092a864886f70d010101050003818d0030818902818100b305185fd816860c4ab2eeccef423e7d5e7cdb450cdef8e02e96422aa12ff32f9844848f8dd07a6f056f3a181e51078a57c2ae865832ef93e9d07de5a4a2633c6e8ce867cf3259f15a7950e91065d0e6809c876199a3fd74558b3e4f9e75c26bea82bed4a66674314562add983067aa927b1e1d75c8403277673d0641858f6170203010001300d06092a864886f70d0101050500038181009e5debf448b10c2f54426f3109aa7c51c95767b608ae968437099109ee001a2cca1c8539dbd75a6265e41025bb5f5beae9d6940d479c6f50b758aee289f08a0ab0eb273ad6adc8806180522f070c5fc242ec3d4a46967513a3c13af95e410a22ae9df833ccd0d15298b239d3c89949b6420df3beefd27c244fdd88465de95cdd" />
<log level="2"/>
</resources>

把自己的签名加进去就ok,后面访问NFC eSE接口的配置文件是/system/etc/nfcee_access.xml

签名的摘要的生成方式

keytool -exportcert -v -keystore my.keystore -alias my_signing_key \ -storepass password|xxd -p -|tr -d '\n'

OMA 访问NFC Service 前置条件

系统权限要求

<uses-permission android:name="org.simalliance.openmobileapi.BIND_TERMINAL"/>

这个权限只有系统应用能申请到,一般安装的应用即使写了申请,最终也申请无效的,这个权限的配置在/system/etc/permission里面声明的,PMS根据声明和权限要求的签名,对apk进行权限赋值。

OMA接口用法

先看具体事例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
package org.simalliance.openmobileapi.test;
import org.simalliance.openmobileapi.Channel;
import org.simalliance.openmobileapi.Reader;
import org.simalliance.openmobileapi.SEService;
import org.simalliance.openmobileapi.SEService.CallBack;
import org.simalliance.openmobileapi.Session;
import android.app.Activity;
import android.os.Bundle;
import android.view.ViewGroup.LayoutParams;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
/**
* Open Mobile API sample to demonstrate how a Secure Element based
* Android application can be realized with the G&D SmartCard API .<br>
*
* SmartCard API implements the SIMalliance Open Mobile API ({@link http://www.simalliance.org}).
*/
public class MainActivity extends Activity {
SEService _service = null;
Session _session = null;
TextView _textview = null;
ScrollView _scrollview = null;
private static final byte[] ISD_AID = new byte[] { (byte) 0xA0, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00 };
private static String bytesToString(byte[] bytes) {
StringBuffer sb = new StringBuffer();
for (byte b : bytes)
sb.append(String.format("%02x ", b & 0xFF));
return sb.toString();
}
private void logText(String message) {
_scrollview.post(new Runnable() {
public void run() {
_scrollview.fullScroll(ScrollView.FOCUS_DOWN);
}
});
_textview.append(message);
}
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LinearLayout layout = new LinearLayout(this);
layout.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
_scrollview = new ScrollView(this);
_textview = new TextView(this);
_textview.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
_scrollview.addView(_textview);
layout.addView(_scrollview);
setContentView(layout);
SEServiceCallback callback = new SEServiceCallback();
new SEService(this, callback);
}
/**
* Callback interface if informs that this SEService is connected to the SmartCardService
*/
public class SEServiceCallback implements CallBack {
public void serviceConnected(SEService service) {
_service = service;
performTest();
}
}
private void performTest() {
Reader[] readers = _service.getReaders();
logText("Available readers: \n");
for (Reader reader : readers)
logText(" " + reader.getName() + " - " + ((reader.isSecureElementPresent()) ? "present" : "absent") + "\n");
if (readers.length == 0) {
logText("No reader available \n");
return;
}
for (Reader reader : readers) {
if (!reader.isSecureElementPresent())
continue;
logText("\n--------------------------------\nSelected reader: \"" + reader.getName() + "\"\n");
try {
_session = reader.openSession();
} catch (Exception e) {
logText(e.getMessage());
}
if (_session == null)
continue;
try {
byte[] atr = _session.getATR();
logText("ATR: " + ((atr == null) ? "unavailable" : bytesToString(atr)) + "\n\n");
} catch (Exception e) {
logText("Exception on getATR(): " + e.getMessage() + "\n\n");
}
testBasicChannel(null);
testBasicChannel(ISD_AID);
testLogicalChannel(null);
testLogicalChannel(ISD_AID);
_session.close();
}
}
void testBasicChannel(byte[] aid) {
try {
logText("BasicChannel test: " + ((aid == null) ? "default applet" : bytesToString(aid)) + "\n");
Channel channel = _session.openBasicChannel(aid);
byte[] cmd = new byte[] { (byte) 0x80, (byte) 0xCA, (byte) 0x9F, 0x7F, 0x00 };
logText(" -> " + bytesToString(cmd) + "\n");
byte[] rsp = channel.transmit(cmd);
logText(" <- " + bytesToString(rsp) + "\n\n");
channel.close();
} catch (Exception e) {
logText("Exception on BasicChannel: " + e.getMessage() + "\n\n");
}
}
void testLogicalChannel(byte[] aid) {
try {
logText("LogicalChannel test: " + ((aid == null) ? "default applet" : bytesToString(aid)) + "\n");
Channel channel = _session.openLogicalChannel(aid);
byte[] cmd = new byte[] { (byte) 0x80, (byte) 0xCA, (byte) 0x9F, 0x7F, 0x00 };
logText(" -> " + bytesToString(cmd) + "\n");
byte[] rsp = channel.transmit(cmd);
logText(" <- " + bytesToString(rsp) + "\n\n");
channel.close();
} catch (Exception e) {
logText("Exception on LogicalChannel: " + e.getMessage() + "\n\n");
}
}
@Override
protected void onDestroy() {
if (_service != null) {
_service.shutdown();
}
super.onDestroy();
}
}

建立Service连接

其实这里是建立与SmartService里面的连接,我们都习惯bindService ,其实这里就是bindService,只不过包装一层用法挺奇怪

new SEService(this, callback);

getReaders

其实这里Android应用程序相当一个读卡器使用人,OMA提供不同的读卡器,根据实现不同,一般有三种,就是图中所以的,每个读卡器对应一个终端,相当扫码枪。我们根据name去分辨,现有的eSE的name 是SmartMX

openSession

reader.openSession()

根据reader的类型建立回话,其实这里只是到了OMA, 通知OMA使用哪个终端。

openChannel

其实这个过程在eSE实现方案中,是OMA和NFC Service建立通讯连接,相当拿到Binder的代理端,方便跨进程使用。

openBasicChannel

基本通道,基本通道的意思说,只有一个,不能并行访问,是独占的,只允许一个apk在一个时间段里面占用基本通道。

openLogicalChannel

逻辑通道,是在基本通道被占的时候,提供并行访问eSE的接口,但是这个逻辑通道只能访问一些特殊的Applet,这个Applet必须实现Shared接口,逻辑通道根据硬件的实现不同可以有多个,可以通过Manage Channel 指令得到具体支持几个逻辑通道

发送APDU

byte[] cmd = new byte[] { (byte) 0x80, (byte) 0xCA, (byte) 0x9F, 0x7F, 0x00 };
logText(" -> " + bytesToString(cmd) + "\n");
byte[] rsp = channel.transmit(cmd);
logText(" <- " + bytesToString(rsp) + "\n\n");

释放资源

-  关闭通道     channel.close();
-  关闭会话 _session.close();
-  解绑服务 _service.shutdown();

OMA接口的具体实现

接口的包装类 SmartMxTerminal

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
/*
* Copyright (C) 2011, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Contributed by: Giesecke & Devrient GmbH.*/
/******************************************************************************
*
* The original Work has been changed by NXP Semiconductors.
*
* Copyright (C) 2015 NXP Semiconductors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
package org.simalliance.openmobileapi.service.terminals;
import android.content.Context;
import com.nxp.eseclient.EseClientManager;
import com.nxp.eseclient.EseClientServicesAdapterBuilder;
import com.nxp.eseclient.EseClientServicesAdapter;
import com.nxp.intf.INxpExtrasService;
import java.io.IOException;
import android.os.Binder;
import android.os.Bundle;
import android.util.Log;
import java.util.MissingResourceException;
import java.util.NoSuchElementException;
import org.simalliance.openmobileapi.service.CardException;
import org.simalliance.openmobileapi.service.SmartcardService;
import org.simalliance.openmobileapi.service.Terminal;
public class SmartMxTerminal extends Terminal {
private static EseClientManager mEseManager;
private static EseClientServicesAdapter mEseClientServicesAdapter;
private static EseClientServicesAdapterBuilder mEseClientServicesAdapterBuilder;
private static INxpExtrasService mINxpExtrasService;
public static int type = EseClientManager.NFC;
private Binder binder = new Binder();
private String TAG = "SmartMxTerminal";
public SmartMxTerminal(Context context) {
super(SmartcardService._eSE_TERMINAL, context);
try{
mEseManager = EseClientManager.getInstance();
mEseManager.initialize();
INxpExtrasService NxpExtrasServiceIntf = null;
mEseClientServicesAdapterBuilder = new EseClientServicesAdapterBuilder();
mEseClientServicesAdapter = mEseClientServicesAdapterBuilder.getEseClientServicesAdapterInstance(type);
NxpExtrasServiceIntf = mEseClientServicesAdapter.getNxpExtrasService();
mINxpExtrasService = NxpExtrasServiceIntf;
}
catch(Exception e)
{
Log.d(TAG, e.getMessage());
}
}
public boolean isCardPresent() throws CardException {
try {
if(mINxpExtrasService == null) {
throw new CardException("Cannot get NFC Default Adapter");
}
return mINxpExtrasService.isEnabled();
} catch (Exception e) {
return false;
}
}
@Override
protected void internalConnect() throws CardException {
if(mINxpExtrasService == null) {
throw new CardException("Cannot get NFC Default Adapter");
}
try {
Bundle b = mINxpExtrasService.open("org.simalliance.openmobileapi.service", binder);
if (b == null) {
throw new CardException("open SE failed");
}
} catch (Exception e) {
throw new CardException("open SE failed");
}
mDefaultApplicationSelectedOnBasicChannel = true;
mIsConnected = true;
}
@Override
protected void internalDisconnect() throws CardException {
try {
Bundle b = mINxpExtrasService.close("org.simalliance.openmobileapi.service", binder);
if (b == null) {
throw new CardException("close SE failed");
}
} catch (Exception e) {
throw new CardException("close SE failed");
}
}
@Override
protected byte[] internalTransmit(byte[] command) throws CardException {
try {
Bundle b = mINxpExtrasService.transceive("org.simalliance.openmobileapi.service", command);
if (b == null) {
throw new CardException("exchange APDU failed");
}
return b.getByteArray("out");
} catch (Exception e) {
throw new CardException("exchange APDU failed");
}
}
@Override
public byte[] getAtr() {
byte uid[] = null;
try {
uid = mINxpExtrasService.getSecureElementUid("org.simalliance.openmobileapi.service");
return uid;
} catch (Exception e) {
Log.d(TAG, e.toString());
Log.d(TAG,"getAtr: get Secure Element Uid failed");
}
return uid;
}
@Override
protected int internalOpenLogicalChannel() throws Exception {
mSelectResponse = null;
byte[] manageChannelCommand = new byte[] {
0x00, 0x70, 0x00, 0x00, 0x01
};
byte[] rsp = transmit(manageChannelCommand, 2, 0x9000, 0, "MANAGE CHANNEL");
if ((rsp.length == 2) && ((rsp[0] == (byte) 0x68) && (rsp[1] == (byte) 0x81))) {
throw new NoSuchElementException("logical channels not supported");
}
if (rsp.length == 2 && (rsp[0] == (byte) 0x6A && rsp[1] == (byte) 0x81)) {
throw new MissingResourceException("no free channel available", "", "");
}
if (rsp.length != 3) {
throw new MissingResourceException("unsupported MANAGE CHANNEL response data", "", "");
}
int channelNumber = rsp[0] & 0xFF;
if (channelNumber == 0 || channelNumber > 19) {
throw new MissingResourceException("invalid logical channel number returned", "", "");
}
return channelNumber;
}
@Override
protected int internalOpenLogicalChannel(byte[] aid) throws Exception {
if (aid == null) {
throw new NullPointerException("aid must not be null");
}
mSelectResponse = null;
byte[] manageChannelCommand = new byte[] {
0x00, 0x70, 0x00, 0x00, 0x01
};
byte[] rsp = transmit(manageChannelCommand, 2, 0x9000, 0, "MANAGE CHANNEL");
if ((rsp.length == 2) && ((rsp[0] == (byte) 0x68) && (rsp[1] == (byte) 0x81))) {
throw new NoSuchElementException("logical channels not supported");
}
if (rsp.length == 2 && (rsp[0] == (byte) 0x6A && rsp[1] == (byte) 0x81)) {
throw new MissingResourceException("no free channel available", "", "");
}
if (rsp.length != 3) {
throw new MissingResourceException("unsupported MANAGE CHANNEL response data", "", "");
}
int channelNumber = rsp[0] & 0xFF;
if (channelNumber == 0 || channelNumber > 19) {
throw new MissingResourceException("invalid logical channel number returned", "", "");
}
byte[] selectCommand = new byte[aid.length + 6];
selectCommand[0] = (byte) channelNumber;
if (channelNumber > 3) {
selectCommand[0] |= 0x40;
}
selectCommand[1] = (byte) 0xA4;
selectCommand[2] = 0x04;
selectCommand[4] = (byte) aid.length;
System.arraycopy(aid, 0, selectCommand, 5, aid.length);
try {
mSelectResponse = transmit(selectCommand, 2, 0x9000, 0xFFFF, "SELECT");
} catch (CardException exp) {
internalCloseLogicalChannel(channelNumber);
throw new NoSuchElementException(exp.getMessage());
}
return channelNumber;
}
@Override
protected void internalCloseLogicalChannel(int channelNumber) throws CardException {
if (channelNumber > 0) {
byte cla = (byte) channelNumber;
if (channelNumber > 3) {
cla |= 0x40;
}
byte[] manageChannelClose = new byte[] {
cla, 0x70, (byte) 0x80, (byte) channelNumber
};
transmit(manageChannelClose, 2, 0x9000, 0xFFFF, "MANAGE CHANNEL");
}
}
}

这里最终通过Binder 到NFC的系统服务里面

INxpExtrasService NxpExtrasServiceIntf = null;
mEseClientServicesAdapterBuilder = new EseClientServicesAdapterBuilder();
mEseClientServicesAdapter = mEseClientServicesAdapterBuilder.getEseClientServicesAdapterInstance(type);
NxpExtrasServiceIntf = mEseClientServicesAdapter.getNxpExtrasService();
mINxpExtrasService = NxpExtrasServiceIntf;

其中 mEseClientServicesAdapter。其实就是NFC的NfcAapter 这里不深入跟踪了 mINxpExtrasService 就是访问eSE的接口代理端。接下来就分析NFC Service里面 eSE相关接口的实现,其实就是一个APDU通道,APDU指令 其实就是和eSE交互的API,一种抽象的接口,在eSE进行TLV解析,去做不同的事情。

1
2
3
4
5
6
7
8
9
public NfcService(Application nfcApplication) {
mUserId = ActivityManager.getCurrentUser();
mContext = nfcApplication;
mNfcTagService = new TagService();
mNfcAdapter = new NfcAdapterService();
mNxpNfcAdapter = new NxpNfcAdapterService();
mExtrasService = new NfcAdapterExtrasService();
mNxpExtrasService = new NxpNfcAdapterExtrasService();

最后一个就是eSE访问的Binder 桩接口 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
final class NxpExtrasService extends INxpExtrasService.Stub {
private Bundle writeNoException() {
Bundle p = new Bundle();
p.putInt("e", 0);
return p;
}
private Bundle writeEeException(int exceptionType, String message) {
Bundle p = new Bundle();
p.putInt("e", exceptionType);
p.putString("m", message);
return p;
}
@Override
public boolean isEnabled() {
try {
Log.e(TAG, "jerry: nfcService isEnabled mState :" + mState);
return (mState == NfcAdapter.STATE_ON);
} catch (Exception e) {
Log.d(TAG, "Exception " + e.getMessage());
return false;
}
}
@Override
public byte[] getSecureElementUid(String pkg) throws RemoteException {
NfcService.this.enforceNfceeAdminPerm(pkg);
return mDeviceHost.getSecureElementUid();
}
@Override
public Bundle open(String pkg, IBinder b) throws RemoteException {
NfcService.this.enforceNfceeAdminPerm(pkg);
Log.d("fine", "open SE: " + pkg);
Bundle result;
int handle = _open(b);
if (handle < 0) {
result = writeEeException(handle, "NFCEE open exception.");
} else {
result = writeNoException();
}
return result;
}
/**
* Opens a connection to the secure element.
*
* @return A handle with a value >= 0 in case of success, or a negative
* value in case of failure.
*/
private int _open(IBinder b) {
synchronized (NfcService.this) {
if (!isNfcEnabled()) {
return EE_ERROR_NFC_DISABLED;
}
if (mInProvisionMode) {
// Deny access to the NFCEE as long as the device is being
// setup
return EE_ERROR_IO;
}
if (mP2pLinkManager.isLlcpActive()) {
// Don't allow PN544-based devices to open the SE while the
// LLCP
// link is still up or in a debounce state. This avoids race
// conditions in the NXP stack around P2P/SMX switching.
return EE_ERROR_EXT_FIELD;
}
if (mOpenEe != null) {
return EE_ERROR_ALREADY_OPEN;
}
boolean restorePolling = false;
if (mNfcPollingEnabled) {
// Disable polling for tags/P2P when connecting to the SMX
// on PN544-based devices. Whenever nfceeClose is called,
// the polling configuration will be restored.
mDeviceHost.disableDiscovery();
mNfcPollingEnabled = false;
restorePolling = true;
}
int handle = doOpenSecureElementConnection();
if (handle < 0) {
if (restorePolling) {
mDeviceHost.enableDiscovery(mCurrentDiscoveryParameters, true);
mNfcPollingEnabled = true;
}
return handle;
}
mDeviceHost.setTimeout(TagTechnology.ISO_DEP, 30000);
mOpenEe = new OpenSecureElement(getCallingPid(), handle, b);
try {
b.linkToDeath(mOpenEe, 0);
} catch (RemoteException e) {
mOpenEe.binderDied();
}
// Add the calling package to the list of packages that have
// accessed
// the secure element.
for (String packageName : mContext.getPackageManager().getPackagesForUid(getCallingUid())) {
mSePackages.add(packageName);
}
return handle;
}
}
@Override
public Bundle close(String pkg, IBinder binder) throws RemoteException {
NfcService.this.enforceNfceeAdminPerm(pkg);
Bundle result;
try {
_nfcEeClose(getCallingPid(), binder);
result = writeNoException();
} catch (IOException e) {
result = writeEeException(EE_ERROR_IO, e.getMessage());
}
return result;
}
@Override
public Bundle transceive(String pkg, byte[] in) throws RemoteException {
NfcService.this.enforceNfceeAdminPerm(pkg);
Bundle result;
byte[] out;
try {
out = _transceive(in);
result = writeNoException();
result.putByteArray("out", out);
} catch (IOException e) {
result = writeEeException(EE_ERROR_IO, e.getMessage());
}
return result;
}
private byte[] _transceive(byte[] data) throws IOException {
synchronized (NfcService.this) {
if (!isNfcEnabled()) {
throw new IOException("NFC is not enabled");
}
if (mOpenEe == null) {
throw new IOException("NFC EE is not open");
}
if (getCallingPid() != mOpenEe.pid) {
throw new SecurityException("Wrong PID");
}
}
return doTransceive(mOpenEe.handle, data);
}
};

其中_开头的都是native方法,最终swp单线协议和eSE单元通讯。下面一层就不深究了。