关于route table变换相关的一些逻辑分析,
从NfcService中的updateServiceState开始分析.
1
@Override
2
public int updateServiceState(int userId , Map serviceState) {
3
android.util.Log.d(TAG,"zhaoyuan updateServiceState");
4
return mCardEmulationManager.updateServiceState(userId ,serviceState);
5
}
到CardEmulationManager中的updateServiceState
1
public int updateServiceState(int userId ,
2
Map<String , Boolean> serviceState) {
3
......
4
Log.d(TAG, "zhaoyuan card updateServiceState");
5
return mServiceCache.updateServiceState(userId ,Binder.getCallingUid() ,serviceState);
6
}
最后到RegisteredServicesCache中的updateServiceState,RegisteredServicesCache就是用来记录跟
踪
HCE或者SE的service的.
1
public int updateServiceState(int userId , int uid,
2
Map<String , Boolean> serviceState) {
3
boolean success = false;
4
//目前组内常用的测试apk,添加完service以后,发现nxpOffHostServiceMap的size都是0.
5
//暂也不清楚这个用途.好像是GMSA测试相关的!
6
HashMap<ComponentName ,ApduServiceInfo> nxpOffHostServiceMap = mRegisteredNxpServicesCache.getApduservicesMaps();
7
......
8
synchronized(mLock) {
9
//serviceState集合就是通过NfcService调用接口时候传入的参数,
10
//一般来说选中/取消都会对一个service进行操作
11
Iterator<Map.Entry<String , Boolean>> it =
12
serviceState.entrySet().iterator();
13
while(it.hasNext()) {
14
Map.Entry<String , Boolean> entry =
15
(Map.Entry<String , Boolean>) it.next();
16
//一般来说key = com.orangelabs.fillroutingtable_OffHost_02/.HceService 全名
17
//value = true/false. 是要进行设置的值.
18
ComponentName componentName = ComponentName.unflattenFromString(entry.getKey());
19
ApduServiceInfo serviceInfo = getService(userId, componentName);
20
if (serviceInfo != null) {
21
int oldState = serviceInfo.getServiceState(CardEmulation.CATEGORY_OTHER);
22
//调用enableService的时候只能是category为other的service.
23
//观察ApduServiceInfo的源码可以发现只有category为other的service才有关于
24
//servicestate的字段和说明
25
//其它的都是调用enableService会直接返回,调用getServiceState时候会会返回
26
//NxpConstants.SERVICE_STATE_ENABLED;
27
serviceInfo.enableService(CardEmulation.CATEGORY_OTHER, entry.getValue());
28
// send IDD info: service state changed.
29
int state = serviceInfo.getServiceState(CardEmulation.CATEGORY_OTHER);
30
if (((state == NxpConstants.SERVICE_STATE_ENABLED)
31
||(state == NxpConstants.SERVICE_STATE_DISABLED))
32
&& (oldState != state)) {
33
boolean isEnabled = (state == NxpConstants.SERVICE_STATE_ENABLED);
34
NfcIddEvent.Gsma.HCESetting.sendServiceStateChanged
35
(componentName.getClassName(), isEnabled);
36
}
37
} else if ((serviceInfo = nxpOffHostServiceMap.get(componentName)) != null) {
38
// CHECK for GSMA cache
39
......
40
}....
41
}
42
//下面会把各个service的状态存入到data/data/com.android.nfc/../service_state.xml中
43
//实现持久化存储,在需要的时候会通过updateServiceStateFromFile进行读取恢复.
44
//!!主次此处的service是用户传入的的要跟新的那些service,category都是other的.
45
success = writeServiceStateToFile(userId);
46
}
47
//调用invalidateCache来更新我们缓存的所有的service状态,注意不只是谁的状态变化处理谁.
48
invalidateCache(ActivityManager.getCurrentUser());
49
return (success?0x00:0xFF);
50
}
1
public void invalidateCache(int userId){
2
//获取所有的service无论是onHost还是offHost.最终吧AndroidManifest中的service封装成了
3
//ApduServiceInfo.
4
final ArrayList<ApduServiceInfo> validServices = getInstalledServices(userId);
5
.......
6
synchronized (mLock) {
7
UserServices userServices = findOrCreateUserLocked(userId);
8
// Find removed services
9
Iterator<Map.Entry<ComponentName, ApduServiceInfo>> it =
10
userServices.services.entrySet().iterator();
11
while (it.hasNext()) {
12
Map.Entry<ComponentName, ApduServiceInfo> entry =
13
(Map.Entry<ComponentName, ApduServiceInfo>) it.next();
14
//如果validServices没有包含entry.getKey()移除掉已经缓存的service.
15
if (!containsServiceLocked(validServices, entry.getKey())) {
16
Log.d(TAG, "Service removed: " + entry.getKey());
17
it.remove();
18
}
19
}
20
for (ApduServiceInfo service : validServices) {
21
//把这些service加入到
22
//final HashMap<ComponentName, ApduServiceInfo> services = Maps.newHashMap();
23
//key = component
24
//value = apduService
25
//此时是所有静态注册的aid,感觉效率不高因为有覆盖的,就是本来就已经存在的.
26
userServices.services.put(service.getComponent(), service);
27
}
28
//下面处理动态注册的,同样是先处理以前已存在,现在不存在的Cache.
29
ArrayList<ComponentName> toBeRemoved = new ArrayList<ComponentName>();
30
for (Map.Entry<ComponentName, DynamicAids> entry :
31
userServices.dynamicAids.entrySet()) {
32
// Verify component / uid match
33
ComponentName component = entry.getKey();
34
DynamicAids dynamicAids = entry.getValue();
35
ApduServiceInfo serviceInfo = userServices.services.get(component);
36
//以前存在,现在不存在的加入到toBeRemoved中.
37
if (serviceInfo == null || (serviceInfo.getUid() != dynamicAids.uid)) {
38
toBeRemoved.add(component);
39
continue;
40
} else {
41
for (AidGroup group : dynamicAids.aidGroups.values()) {
42
//放到ApduServiceInfo中的mDynamicAidGroups当中
43
serviceInfo.setOrReplaceDynamicAidGroup(group);
44
}
45
}
46
}
47
if (toBeRemoved.size() > 0) {
48
for (ComponentName component : toBeRemoved) {
49
userServices.dynamicAids.remove(component);//更新缓存
50
}
51
// Persist to filesystem
52
//更新当前apk数据文件夹下的dynamic_aids.xml当中的状态.
53
writeDynamicAidsLocked();
54
}
55
//更新writeServiceStateToFile时保存在文件中的service_state.xml.
56
updateServiceStateFromFile(userId);
57
//保存新的service的状态
58
writeServiceStateToFile(userId);
59
}
60
//mCallback对应CardEmulationManager,特别主次此时传入的services是全部的在
61
//AndroidManifest中注册的可用的service.
62
mCallback.onServicesUpdated(userId, Collections.unmodifiableList(validServices));
63
}
1
//getInstalledServices详解
2
ArrayList<ApduServiceInfo> getInstalledServices(int userId){
3
PackageManager pm;
4
try {
5
pm = mContext.createPackageContextAsUser("android", 0,
6
new UserHandle(userId)).getPackageManager();
7
} catch (NameNotFoundException e) {
8
Log.e(TAG, "Could not create user package context");
9
return null;
10
}
11
mAllServices.clear();
12
ArrayList<ApduServiceInfo> validServices = new ArrayList<ApduServiceInfo>();
13
14
//获取onHost的service
15
List<ResolveInfo> resolvedServices = pm.queryIntentServicesAsUser(
16
new Intent(HostApduService.SERVICE_INTERFACE),
17
PackageManager.GET_META_DATA, userId);
18
//获取offHost的service
19
List<ResolveInfo> resolvedOffHostServices = pm.queryIntentServicesAsUser(
20
new Intent(OffHostApduService.SERVICE_INTERFACE),
21
PackageManager.GET_META_DATA, userId);
22
//组合到一块
23
resolvedServices.addAll(resolvedOffHostServices);
24
for (ResolveInfo resolvedService : resolvedServices) {
25
try {
26
boolean onHost = !resolvedOffHostServices.contains(resolvedService);
27
ServiceInfo si = resolvedService.serviceInfo;
28
ComponentName componentName = new ComponentName(si.packageName, si.name);
29
//看看是不是有相关的权限
30
if (pm.checkPermission(android.Manifest.permission.NFC, si.packageName) !=
31
PackageManager.PERMISSION_GRANTED) {
32
......;
33
continue;
34
}
35
if (!android.Manifest.permission.BIND_NFC_SERVICE.equals(
36
si.permission)) {
37
continue;
38
}
39
//把从AndroidMnifest中解析出来的service相关的信息,封装成ApduServiceInfo.
40
ApduServiceInfo service = new ApduServiceInfo(pm, resolvedService, onHost);
41
if (service != null) {
42
validServices.add(service);
43
if(!onHost){
44
//mAllServices反而放置的都是offHost的service.
45
mAllServices.put(componentName, service);
46
}
47
}
48
}...
49
}
50
AddGsmaServices(validServices); //???不太清楚,应该是和GSMA测试的时候相关的.
51
return validServices;//返回所有静态注册的
52
}
1
//findOrCreateUserLocked有个相关的数据结构很关键,就是UserServices
2
private UserServices findOrCreateUserLocked(int userId) {
3
UserServices services = mUserServices.get(userId);
4
if (services == null) {
5
services = new UserServices();
6
//mUserServices这个就是包含在所有user下运行的card emulation services.
7
mUserServices.put(userId, services);
8
}
9
return services;
10
}
对于每个user来说有一个UserServices用来存储service
1
private static class UserServices {
2
//存放的是所有的注册的service,无论是on/off Host的,注意和动态/静态注册的aid
3
//没什么关系,这边说的是service!AndroidManifest中的service,无论AID是一开始
4
//就动态注册还是运行中动态注册的,你这些service是肯定需要在AndroidManifest中注册的.
5
final HashMap<ComponentName, ApduServiceInfo> services =
6
Maps.newHashMap(); // Re-built at run-time
7
//动态注册的
8
final HashMap<ComponentName, DynamicAids> dynamicAids =
9
Maps.newHashMap(); // In memory cache of dynamic AID store
10
};
然后再简单看一下service_state.xml和dynamic_aids.xml中的数据的基本形式.
service_state.xml如下:
1
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
2
<Version FileVersion="1.0" />
3
<services>
4
<service component="com.orange.labs.dynamicotherhostregistrationapp/com.orange.
5
labs.dynamicotherhostregistrationapp.HostService" uid="10187"
6
serviceState="1" />
7
<service component="com.orangelabs.fillroutingtable_OffHost_02/com.orangelabs.
8
fillroutingtable_OffHost_02.HceService" uid="10192" serviceState="0" />
9
<service component="com.orange.labs.dynamicotheroffhostregistrationapp/
10
com.orange.labs.dynamicotheroffhostregistrationapp.OffHostService"
11
uid="10188" serviceState="0" />
12
</services>
dynamic_aids.xml如下:
1
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
2
<services>
3
<service component="com.orange.labs.dynamicotherhostregistrationapp/com
4
.orange.labs.dynamicotherhostregistrationapp.HostService" uid="10187">
5
<aid-group category="other">
6
<aid value="AAFFFFFFFFFFFFFFFFFFFFFFFFFFFF00" />
7
<aid value="AAFFFFFFFFFFFFFFFFFFFFFFFFFFFF01" />
8
<aid value="AAFFFFFFFFFFFFFFFFFFFFFFFFFFFF02" />
9
<aid value="AAFFFFFFFFFFFFFFFFFFFFFFFFFFFF03" />
10
<aid value="AAFFFFFFFFFFFFFFFFFFFFFFFFFFFF04" />
11
</aid-group>
12
</service>
13
</services>
最后调用的mCallback的实现是如下:
1
public class CardEmulationManager implements RegisteredServicesCache.Callback,
2
RegisteredNfcFServicesCache.Callback, PreferredServices.Callback,
3
EnabledNfcFServices.Callback {
4
@Override
5
public void onServicesUpdated(int userId, List<ApduServiceInfo> services) {
6
//验证更新一下default service.
7
verifyDefaults(userId, services);
8
//更新AidCache
9
mAidCache.onServicesUpdated(userId, services);
10
// Update the preferred services list
11
mPreferredServices.onServicesUpdated();
12
}
1
void verifyDefaults(int userId, List<ApduServiceInfo> services) {
2
//获取defaultPaymentService,注意category是payment的,就是去Settings.Secure数据库中获取对应字段.
3
ComponentName defaultPaymentService =
4
getDefaultServiceForCategory(userId, CardEmulation.CATEGORY_PAYMENT, false);
5
//当defaultPaymentService为null的时候,当自己安装两个,把其中一个设置成default,然后再把它卸载以后
6
//虽然设置显示unset,但是Log中此处依然是先前的值.
7
if (defaultPaymentService == null) {
8
//看如下英文注释.
9
// A payment service may have been removed, leaving only one;
10
// in that case, automatically set that app as default.
11
int numPaymentServices = 0;
12
ComponentName lastFoundPaymentService = null;
13
//遍历要update的service.
14
for (ApduServiceInfo service : services) {
15
if (service.hasCategory(CardEmulation.CATEGORY_PAYMENT)) {
16
numPaymentServices++;
17
lastFoundPaymentService = service.getComponent();
18
}
19
}
20
if (numPaymentServices > 1) {
21
// More than one service left, leave default unset
22
if (DBG) Log.d(TAG, "No default set, more than one service left.");
23
} else if (numPaymentServices == 1) {
24
// Make single found payment service the default
25
if (isUnregistrablePayment(userId, lastFoundPaymentService)) {
26
if (DBG) Log.d(TAG, "Not set default since the payment caused overflow");
27
return;
28
}
29
//往数据库的字段中存入.
30
if (DBG) Log.d(TAG, "No default set, making single service default.");
31
setDefaultServiceForCategoryChecked(userId, lastFoundPaymentService,
32
CardEmulation.CATEGORY_PAYMENT);
33
} else {
34
// No payment services left, leave default at null
35
if (DBG) Log.d(TAG, "No default set, last payment service removed.");
36
}
37
}
38
}
RegisteredAidCache中的onServicesUpdated.
RegisteredAidCache是用来跟踪记录系统中services注册的所有的aids,管理在NFCC中的routing table.
1
public void onServicesUpdated(int userId, List<ApduServiceInfo> services) {
2
synchronized (mLock) {
3
if (ActivityManager.getCurrentUser() == userId) {
4
mPreviousPaymentService = mPreferredPaymentService;
5
// Rebuild our internal data-structures
6
//就是建立一些系统内需要使用的关于AID的相关的数据存储
7
generateServiceMapLocked(services);
8
generateAidCacheLocked();
9
} ...
10
}
11
}
1
//此方法最重要的就是要处理mAidServices这个变量,方便后面使用。
2
// final TreeMap<String, ArrayList<ServiceAidInfo>> mAidServices =
3
// new TreeMap<String, ArrayList<ServiceAidInfo>>();
4
//String 是aid,ArrayList<ServiceAidInfo>是aid注册的service的集合.
5
void generateServiceMapLocked(List<ApduServiceInfo> services) {
6
//清空mAidServices
7
mAidServices.clear();
8
for (ApduServiceInfo service : services) {
9
//获取当前service注册的所有的prefix的aid,就是以*结尾的.
10
//注意一个service可以注册多个aid
11
List<String> prefixAids = service.getPrefixAids();
12
for (String aid : service.getAids()) {
13
......
14
//看看是不是prefix的AID
15
if (aid.endsWith("*") && !supportsAidPrefixRegistration()) {
16
//不支持部分匹配的时候
17
continue;
18
} else if (supportsAidPrefixRegistration() && prefixAids.size() > 0 &&
19
!isPrefix(aid)) {
20
// Check if we already have an overlapping prefix registered for this AID
21
//支持AidPrefix,并且prefixAids已经有了,并且当前的aid不是prefix的,那就证明此时
22
//对于这个aid既有prefix,也有exact.
23
//如:1234567892* 和12345678926548
24
boolean foundPrefix = false;
25
for (String prefixAid : prefixAids) {
26
String prefix = prefixAid.substring(0, prefixAid.length() - 1);
27
//如果当前的aid是prefix的
28
if (aid.startsWith(prefix)) {
29
foundPrefix = true;
30
break;
31
}
32
}
33
//不处理,防止重复处理,就是都按找prefix的处理了,不处理exact的了.
34
if (foundPrefix) {
35
continue;
36
}
37
}
38
//封装的ServiceAidInfo,代表当前aid所在的service和category.
39
ServiceAidInfo serviceAidInfo = new ServiceAidInfo();
40
serviceAidInfo.service = service;//当前aid所属的service
41
serviceAidInfo.category = service.getCategoryForAid(aid);
42
if( (serviceAidInfo.category.equals(CardEmulation.CATEGORY_OTHER)) &&
43
((service.getServiceState(CardEmulation.CATEGORY_OTHER) ==
44
NxpConstants.SERVICE_STATE_DISABLED) ||
45
(service.getServiceState(CardEmulation.CATEGORY_OTHER) ==
46
NxpConstants.SERVICE_STATE_DISABLING))){
47
/*Do not include the services which are already disabled Or
48
*services which are disabled by user recently
49
* for the current commit to routing table*/
50
Log.e(TAG, "registration because service category is disabled");
51
continue;
52
}
53
//NXP specific, Adding prefix (*) to all off host aid for prefix match.
54
if (mRoutingManager.getAidMatchingPlatform() == AidRoutingManager.AID_MATCHING_K
55
&& !service.isOnHost() && !aid.endsWith("*")) {
56
aid = aid + "*";
57
}
58
serviceAidInfo.aid = aid.toUpperCase();
59
//mAidServices :
60
//key = aid ;
61
// value = ArrayList<ServiceAidInfo>;所有注册这个aid的service的封装.
62
if (mAidServices.containsKey(serviceAidInfo.aid)) {
63
//如果当前aid已经存在,就是不仅注册了一个service.
64
final ArrayList<ServiceAidInfo> serviceAidInfos =
65
mAidServices.get(serviceAidInfo.aid);
66
serviceAidInfos.add(serviceAidInfo);
67
} else {
68
final ArrayList<ServiceAidInfo> serviceAidInfos =
69
new ArrayList<ServiceAidInfo>();
70
serviceAidInfos.add(serviceAidInfo);
71
//每个aid和它对应的services的集合加入到mAidServices当中.
72
mAidServices.put(serviceAidInfo.aid, serviceAidInfos);
73
}
74
}
75
}
76
}
1
//此方法主要是处理mAidCache相关的变量存储,使每个aid resolved,最后调用
2
//updateRoutingLocked更新route.
3
void generateAidCacheLocked(){
4
mAidCache.clear();
5
//前面方法得到所有的exact和prefix的AIDs.封装成一个PriorityQueue的数据类型.
6
PriorityQueue<String> aidsToResolve = new PriorityQueue<String>(mAidServices.keySet());
7
//然后循环的去取
8
while (!aidsToResolve.isEmpty()) {
9
final ArrayList<String> resolvedAids = new ArrayList<String>();
10
//取出来一个
11
String aidToResolve = aidsToResolve.peek();
12
......
13
//如果是Prefix的
14
if (isPrefix(aidToResolve)) {
15
//注意需要有root AID和children AIDs
16
// For example, if "A000000003*" is the prefix root,
17
// "A000000003", "A00000000301*", "A0000000030102"
18
//are all conflicting children AIDs
19
final ArrayList<ServiceAidInfo> prefixServices = new ArrayList<ServiceAidInfo>(
20
mAidServices.get(aidToResolve)); //先取出来这个aid对应的所有的service.
21
//找到所有冲突(注册的是同一个service)的aid,进行相关变量的保存.
22
PrefixConflicts prefixConflicts = findConflictsForPrefixLocked(aidToResolve);
23
//解决冲突,就是按照前面说的策略,看把这些冲突的service那个设置成default的.
24
AidResolveInfo resolveInfo = resolvePrefixAidConflictLocked(prefixServices,
25
prefixConflicts.services);
26
mAidCache.put(aidToResolve, resolveInfo);
27
resolvedAids.add(aidToResolve);
28
//当已经解决冲突的时候
29
if (resolveInfo.defaultService != null) {
30
resolvedAids.addAll(prefixConflicts.aids);
31
} else if (resolveInfo.services.size() > 0) {
32
// This means we don't have a default for this prefix and all its
33
// conflicting children. So, for all conflicting AIDs, just add
34
// all handling services without setting a default
35
boolean foundChildService = false;
36
for (Map.Entry<String, ArrayList<ServiceAidInfo>> entry :
37
prefixConflicts.conflictMap.entrySet()) {
38
if (!entry.getKey().equalsIgnoreCase(aidToResolve)) {
39
if (DBG)
40
Log.d(TAG, "AID " + entry.getKey() + " shared with prefix; " +
41
" adding all handling services.");
42
AidResolveInfo childResolveInfo = resolveAidConflictLocked(
43
entry.getValue(), false);
44
// Special case: in this case all children AIDs must be routed to the
45
// host, so we can ask the user which service is preferred.
46
// Since these are all "children" of the prefix, they don't need
47
// to be routed, since the prefix will already get routed to the host
48
childResolveInfo.mustRoute = false;
49
mAidCache.put(entry.getKey(),childResolveInfo);
50
resolvedAids.add(entry.getKey());
51
foundChildService |= !childResolveInfo.services.isEmpty();
52
}
53
}
54
// Special case: if in the end we didn't add any children services,
55
// and the prefix has only one service, make that default
56
if (!foundChildService && resolveInfo.services.size() == 1) {
57
resolveInfo.defaultService = resolveInfo.services.get(0);
58
}
59
} else {
60
// This prefix is not handled at all; we will evaluate
61
// the children separately in next passes.
62
}
63
} else {
64
// 当是exact的时候,直接根据aid取出所有待处理的ServiceAidInfo集合.
65
final ArrayList<ServiceAidInfo> conflictingServiceInfos =
66
new ArrayList<ServiceAidInfo>(mAidServices.get(aidToResolve));
67
//final TreeMap<String, AidResolveInfo> mAidCache = new TreeMap<String,
68
//AidResolveInfo>();
69
//最后都加入到了AidCache当中key = aid;value = AidResolveInfo.
70
//AidResolveInfo类代表当前aid要route的services,以及default service和category
71
mAidCache.put(aidToResolve, resolveAidConflictLocked(conflictingServiceInfos,
72
true));
73
resolvedAids.add(aidToResolve);
74
}
75
76
// Remove the AIDs we resolved from the list of AIDs to resolve
77
if (DBG) Log.d(TAG, "AIDs: " + resolvedAids + " were resolved.");
78
//is resolve then remove it, attend is a temp,until it is null
79
//然后移除已经处理好的
80
aidsToResolve.removeAll(resolvedAids);
81
resolvedAids.clear();
82
}
83
//AID都加入到mAidCache当中后,再继续执行如下
84
//final TreeMap<String, AidResolveInfo> mAidCache = new TreeMap<String, AidResolveInfo>();
85
updateRoutingLocked();
86
}
1
//关于PrefixConflicts类
2
final class PrefixConflicts {
3
//整体的一个导航,存放所有
4
NavigableMap<String, ArrayList<ServiceAidInfo>> conflictMap;
5
//存放同一个aid对应的所有service
6
final ArrayList<ServiceAidInfo> services = new ArrayList<ServiceAidInfo>();
7
//prefixAid对应的所有的aids,包括root和child.
8
final HashSet<String> aids = new HashSet<String>();
9
}
1
//关于resolvePrefixAidConflictLocked.
2
AidResolveInfo resolvePrefixAidConflictLocked(ArrayList<ServiceAidInfo> prefixServices,
3
ArrayList<ServiceAidInfo> conflictingServices) {
4
......
5
}
1
//关于resolveAidConflictLocked
2
/**
3
* Resolves a conflict between multiple services handling the same
4
* AIDs.
5
* it works likethis:
6
* 1) If there is a preferred foreground service, that service wins
7
* 2) Else, if there is a preferred payment service, that service wins
8
* 3) Else, if there is no winner, and all conflicting services will be
9
* in the list of resolved services.
10
*/
11
AidResolveInfo resolveAidConflictLocked(Collection<ServiceAidInfo> conflictingServices,
12
boolean makeSingleServiceDefault) {
13
...//合法性判断.
14
AidResolveInfo resolveInfo = new AidResolveInfo();
15
//默认的category是other.
16
resolveInfo.category = CardEmulation.CATEGORY_OTHER;
17
18
//代表此时运行在前台的组件.
19
ApduServiceInfo matchedForeground = null;
20
//代表default payment.
21
ApduServiceInfo matchedPayment = null;
22
for (ServiceAidInfo serviceAidInfo : conflictingServices) {//遍历
23
//用来表示是否是payment.
24
boolean serviceClaimsPaymentAid =
25
CardEmulation.CATEGORY_PAYMENT.equals(serviceAidInfo.category);
26
//下面就是按照前面的1\2\3的策略来处理.
27
if (serviceAidInfo.service.getComponent().equals(mPreferredForegroundService)) {
28
resolveInfo.services.add(serviceAidInfo.service);
29
if (serviceClaimsPaymentAid) {
30
resolveInfo.category = CardEmulation.CATEGORY_PAYMENT;
31
}
32
matchedForeground = serviceAidInfo.service;
33
} else if (serviceAidInfo.service.getComponent().equals(mPreferredPaymentService) &&
34
serviceClaimsPaymentAid) {
35
resolveInfo.services.add(serviceAidInfo.service);
36
resolveInfo.category = CardEmulation.CATEGORY_PAYMENT;
37
matchedPayment = serviceAidInfo.service;
38
} else {
39
if (serviceClaimsPaymentAid) {
40
// If this service claims it's a payment AID, don't route it,
41
// because it's not the default. Otherwise, add it to the list
42
// but not as default.
43
//当前AID的category是payment的,并且当前注册这个aid的service不是default payment.
44
//那么它对应的这个serviceAidInfo将不进行任何处理,也就是不route aid.
45
} else {
46
//其它情况全部添加到这里.此时都是other的.
47
resolveInfo.services.add(serviceAidInfo.service);
48
}
49
}
50
}
51
//所有的conflictingServices遍历完毕.
52
if (matchedForeground != null) {
53
// 1st priority: if the foreground app prefers a service,
54
// and that service asks for the AID, that service gets it
55
if (DBG) Log.d(TAG, "resolveAidLocked: DECISION: routing to foreground preferred " +
56
matchedForeground);
57
resolveInfo.defaultService = matchedForeground;
58
} else if (matchedPayment != null) {
59
// 2nd priority: if there is a preferred payment service,
60
// and that service claims this as a payment AID, that service gets it
61
if (DBG) Log.d(TAG, "resolveAidLocked: DECISION: routing to payment default " +
62
"default " + matchedPayment);
63
resolveInfo.defaultService = matchedPayment;
64
} else {
65
//这个其实就是对应的exact的aid,此时只有一个service处理。就把它设置为default.
66
//此时category是other的就会设置一个default.
67
if (resolveInfo.services.size() == 1 && makeSingleServiceDefault) {
68
if (DBG) Log.d(TAG, "resolveAidLocked: DECISION: making single handling service "
69
+ resolveInfo.services.get(0).getComponent() + " default.");
70
resolveInfo.defaultService = resolveInfo.services.get(0);
71
} else {
72
// Nothing to do, all services already in list
73
if (DBG) Log.d(TAG, "resolveAidLocked: DECISION: routing to all matching
74
services");
75
}
76
}
77
return resolveInfo;
78
}
1
//AidResolveInfo相关
2
// Represents a list of services, an optional default and a category that
3
// an AID was resolved to.
4
final class AidResolveInfo {
5
//Aid要route到的services.
6
List<ApduServiceInfo> services = new ArrayList<ApduServiceInfo>();
7
ApduServiceInfo defaultService = null;//当前aid默认要route的service
8
String category = null;
9
boolean mustRoute = true; // Whether this AID should be routed at all
10
11
@Override
12
public String toString() {
13
return "AidResolveInfo{" +
14
"services=" + services +
15
", defaultService=" + defaultService +
16
", category='" + category + '\'' +
17
", mustRoute=" + mustRoute +
18
'}';
19
}
20
}
1
void updateRoutingLocked(){
2
......
3
//主要就上往下面这个routingEntries,添加aid和对应的AidElement.
4
final HashMap<String, AidElement> routingEntries = Maps.newHashMap();
5
/对于每个AID去找到对它感兴趣的service,就是注册它的AID.mAidCache里面是我们前面准备好的数据结构
6
for (Map.Entry<String, AidResolveInfo> aidEntry:
7
mAidCache.entrySet()) {
8
//取出来一个AID
9
String aid = aidEntry.getKey();
10
//获取AID对应的AidResolveInfo
11
AidResolveInfo resolveInfo = aidEntry.getValue();
12
......
13
if (resolveInfo.services.size() == 0) {
14
//没有任何service感兴趣当前AID的时候,如当前aid是payment的,但不是默认的此时就会是0.
15
//此时aid不会添加到routingEntries,也就是不会被route.
16
} else if (resolveInfo.defaultService != null) {
17
//当这个AID有一个defaultService可以去route的时候.
18
ApduServiceInfo.ESeInfo seInfo = resolveInfo.defaultService.getSEInfo();
19
//取出各个信息.
20
boolean isDefaultPayment =
21
resolveInfo.defaultService.getComponent().equals(mPreferredPaymentService);
22
boolean isForeground =
23
resolveInfo.defaultService.getComponent().equals(mPreferredForegroundService);
24
boolean isOnHost = resolveInfo.defaultService.isOnHost();
25
boolean isPaymentAid =
26
resolveInfo.category.equals(CardEmulation.CATEGORY_PAYMENT);
27
int route;
28
int vzwPowerstate =0 ;
29
//powerstate的设置
30
int powerstate = seInfo.getPowerState() & POWER_STATE_ALL;
31
if(powerstate == 0)
32
{
33
powerstate |= POWER_STATE_SWITCH_ON;
34
if (!isOnHost) {
35
powerstate |= POWER_STATE_SWITCH_OFF;
36
}
37
}
38
int weight = AidElement.ROUTE_WIEGHT_OTHER;
39
if (!isOnHost) {
40
if (DBG) Log.d(TAG," set screen off enable for " + aid);
41
powerstate |= (powerstate | 0x80);
42
}
43
powerstate |= 0x40;
44
......
45
//onHost的route table是0,
46
route = isOnHost ? 0 : seInfo.getSeId();
47
if (isForeground) {
48
weight += AidElement.ROUTE_WIEGHT_FOREGROUND;
49
}
50
if (isDefaultPayment && isPaymentAid) {
51
weight += AidElement.ROUTE_WIEGHT_PAYMENT;
52
}
53
//把Aid封装成AidElement,主要包括aid、和rotue方向,以及一些附带状态.
54
AidElement aidElem = new AidElement(aid, weight, route, powerstate);
55
//放到routingEntries当中,注意这个routingEntries是在当前的方法中实例化的.
56
routingEntries.put(aid, aidElem);
57
} else if (resolveInfo.services.size() == 1) {
58
//好像很难进去,因为在前面resolveAidConflictLocked的时候会为当前Aid设置一个default.
59
//注意此处default指当前aid对应的AidResolveInfo.defaultService而不是default payment.
60
//算是这个aid要default route的service.
61
// Only one service, but not the default, must route to host
62
// to ask the user to choose one.
63
AidElement aidElem = new AidElement(aid, AidElement.ROUTE_WIEGHT_OTHER, 0,
64
mHostAIDPowerState);
65
routingEntries.put(aid, aidElem);
66
} else if (resolveInfo.services.size() > 1) {
67
//这个应该就是多个service注册相同搞得aid的时候
68
// Multiple services, need to route to host to ask
69
AidElement aidElem = new AidElement(aid, AidElement.ROUTE_WIEGHT_OTHER, 0,
70
mHostAIDPowerState);
71
routingEntries.put(aid, aidElem);
72
}
73
}
74
//所有的Aid都处理完以后.
75
mRoutingManager.configureRouting(routingEntries);
76
}
1
//配置route的地方,位于AidRoutingManager,传入所有要route的aid和对应的AidElement.
2
public boolean configureRouting(HashMap<String, AidElement> aidMap){
3
mRoutingTableChanged = false;
4
mDefaultRoute = NfcService.getInstance().GetDefaultRouteLoc();
5
mDefaultRoute = NfcCertDebugModeUtil.getDefaultRouteLoc(mDefaultRoute);
6
boolean aidRouteResolved = false;
7
mAidRoutingTableSize = NfcService.getInstance().getAidRoutingTableSize();
8
//限制为50,存放的是所有route方向是non-default的aid(老感觉这个会不会限制本身的size啊).
9
Hashtable<String, AidElement> routeCache = new Hashtable<String, AidElement>(50);
10
//以key = route(要route的方向),value = 每个route对应的所有的aids.
11
SparseArray<Set<String>> aidRoutingTable = new SparseArray<Set<String>>(aidMap.size());
12
HashMap<String, Integer> routeForAid = new HashMap<String, Integer>(aidMap.size());
13
HashMap<String, Integer> powerForAid = new HashMap<String, Integer>(aidMap.size());
14
// Then, populate internal data structures first
15
//defaultRouteCache是用来帮助计算、切换route table的.
16
DefaultAidRouteResolveCache defaultRouteCache = new DefaultAidRouteResolveCache();
17
//遍历所有的aid
18
for (Map.Entry<String, AidElement> aidEntry : aidMap.entrySet()) {
19
AidElement elem = aidEntry.getValue();
20
int route = elem.getRouteLocation();
21
int power = elem.getPowerState();
22
if (route == -1 ) {
23
route = mDefaultOffHostRoute;
24
elem.setRouteLocation(route);
25
}
26
String aid = aidEntry.getKey();
27
//根据route取出aid集合entries.
28
Set<String> entries = aidRoutingTable.get(route, new HashSet<String>());
29
//往entry中添加aid
30
entries.add(aid);
31
//把整理好的数据,添加到集合中. aidRoutingTable中就可以理解成有好几个表
32
//每一个route方向是一个表,然后这个表上面有很多个aids,是route到指定方向的.
33
aidRoutingTable.put(route, entries);
34
routeForAid.put(aid, route);
35
powerForAid.put(aid, power);
36
}
37
synchronized (mLock) {
38
//aid和route完全没有任何变化的时候.
39
if (routeForAid.equals(mRouteForAid)) {
40
if (DBG) Log.d(TAG, "Routing table unchanged, but commit the routing");
41
if(mLastCommitStatus == false){
42
// There is a possibility to run this line even no any services change
43
// onRoutingUpdated return true only if any services are updated
44
//这个update是看看有没有fail的service
45
if (NfcService.getInstance().onRoutingUpdated()) {
46
NfcService.getInstance().updateStatusOfServices(false);
47
Log.d(TAG, "Routing table unchanged, notifyRoutingTableFull");
48
//此时两边都满了,产生Overflow进行弹框提示.
49
NfcService.getInstance().notifyRoutingTableFull();
50
}
51
}
52
else
53
{/*If last commit status was success, And a new service is added whose AID's are
54
already resolved by previously installed services, service state of newly
55
installed app needs to be updated*/
56
NfcService.getInstance().updateStatusOfServices(true);
57
}
58
if (isProcessingTapAgain()) {
59
if (DBG) Log.d(TAG, "Routing table unchanged, but commit the routing");
60
NfcService.getInstance().commitRouting();
61
} else {
62
if (DBG) Log.d(TAG, "Routing table unchanged, not updating");
63
}
64
return false;
65
}
66
//当route table 发生改变的时候
67
mRoutingTableChanged = true;
68
//clean mRouteForAid and mPowerForAid
69
clearNfcRoutingTableLocked();
70
//前面准备好的数值,依次赋值.
71
mRouteForAid = routeForAid;
72
mPowerForAid = powerForAid;
73
mAidRoutingTable = aidRoutingTable;
74
//直接进行对三个route方向的遍历.
75
for(int routeIndex=0; routeIndex < 0x03; routeIndex++) {
76
//可以看到每个index都有不同的routeCache来存放aid
77
//这个routeCache是在这个方法中实例化的,Hashtable<String, AidElement>(50);
78
routeCache.clear();
79
if (mAidMatchingSupport == AID_MATCHING_PREFIX_ONLY ||
80
mAidMatchingPlatform == AID_MATCHING_K) {
81
......//此处不清楚.
82
}
83
//遍历routingtable,此处是我们前面加入的真是的route表.
84
for (int i = 0; i < mAidRoutingTable.size(); i++) {
85
//得到当前index对应的route
86
int route = mAidRoutingTable.keyAt(i);
87
//当route不是default的时候,default route可以随便加aid,反正溢出的aid都会走
88
//default
89
//这样就回到正确的route方向,此处只处理非default的.
90
//再结合此处就是往routeCache中添加aid,也就是说routeCache中缓存的
91
//route方向都是non-default的aid.
92
if (route != mDefaultRoute) {
93
//取出来当前route下的所有的aid.
94
Set<String> aidsForRoute = mAidRoutingTable.get(route);
95
for (String aid : aidsForRoute) {//遍历
96
if (aid.endsWith("*")) {//如果是prefix的.
97
if (mAidMatchingSupport == AID_MATCHING_EXACT_ONLY) {
98
Log.e(TAG, "This device does not support prefix AIDs.");
99
} else if (mAidMatchingSupport == AID_MATCHING_PREFIX_ONLY) {
100
if (DBG) Log.d(TAG, "Routing prefix AID " + aid + " to route "
101
+ Integer.toString(route));
102
AidElement elem = aidMap.get(aid);
103
elem.setAid(aid.substring(0,aid.length() - 1));
104
routeCache.put(aid, elem);
105
} else if (mAidMatchingSupport == AID_MATCHING_EXACT_OR_PREFIX) {
106
Log.d(TAG, "Routing AID in AID_MATCHING_EXACT_OR_PREFIX");
107
if (DBG) Log.d(TAG, "Routing prefix AID" + aid + " to route "
108
+ Integer.toString(route));
109
AidElement elem = aidMap.get(aid);
110
routeCache.put(aid, elem);
111
}
112
} else {
113
//Routing exact AID
114
AidElement elem = aidMap.get(aid);
115
//最终的核心就是放入到routeCache中
116
routeCache.put(aid, elem);
117
}
118
}
119
}
120
}//结束所有routingtable的遍历,完成routeCache添加.
121
122
//注意此时依然是在遍历route的index的循环中0~3.
123
//当routeIndex是host也就是0,并且routeCache没有超过table size的时候
124
//感觉此处不是为了说它是Host,应该是为了表述,当次循环第一次到这里的时候,看看routeCache
125
//有没有大于mAidRoutingTableSize.此时不需要route改变什么的,直接退出循环.
126
if((routeIndex == 0x00) && (defaultRouteCache.
127
calculateAidRouteSize(routeCache) <= mAidRoutingTableSize)) {
128
// maximum aid table size is less than AID route table size
129
aidRouteResolved = true;
130
break;//退出
131
} else {
132
//两种情况会走到这里:
133
// 1)、routeIndex不等于0,就是循环最少已经是第二次来到这个判断了.
134
// 此时也肯定前面第一次查询到routeCache超出size了
135
// 2)、routeIndex等于0,但是routeCache的size超过table size的时候
136
//更新mDefaultRoute的routeCache和routeSize到defaultRouteCache对应的变量.
137
defaultRouteCache.updateDefaultAidRouteCache(routeCache,mDefaultRoute);
138
//当是第一次且发生溢出的时候,走到这里.进行DefaultRoute切换,通过getNextRouteLoc
139
//就是从0~3依次尝试设置(non-default的),
140
mDefaultRoute = defaultRouteCache.getNextRouteLoc();
141
//此时假如第一次产生default route溢出的时候,再去接着循环,但defaultrout变了.
142
if(mDefaultRoute == 0xFF)
143
{
144
break;
145
}
146
}
147
}//routeindex循环结束.
148
}
149
//当没有成功Resolved的时候
150
if(aidRouteResolved == false) {
151
if(mAidRoutingTable.size() == 0x00) {
152
aidRouteResolved = true;
153
} else if(defaultRouteCache.resolveDefaultAidRoute() == true) {//计算有没有全部溢出
154
//当一个table如:host/UICC发生 overflow的时候,有前面可以看到只有在0x00时候才有true
155
//而当0x00的时候Host就发生了溢出,那么接下来循环完,也不会有设置为true的地方,就到了这里
156
aidRouteResolved = true;
157
routeCache = defaultRouteCache.getResolvedAidRouteCache();
158
NfcService.getInstance().
159
setDefaultAidRouteLoc(defaultRouteCache.getResolvedAidRoute());
160
} else {
161
//当UICC和Host都发生overflow的时候
162
NfcService.getInstance().onRoutingUpdated();
163
NfcIddEvent.Gsma.Overflow.sendOverflow();
164
//弹框提示用户
165
NfcService.getInstance().notifyRoutingTableFull();
166
}
167
}
168
// And finally commit the routing and update the status of commit for each service
169
if(aidRouteResolved == true) {
170
//commit的都是non-default route的,
171
//难道default在applyroute的时候重新discovery会自动init???
172
commit(routeCache);
173
NfcService.getInstance().updateStatusOfServices(true);
174
mLastCommitStatus = true;
175
}else{
176
NfcService.getInstance().updateStatusOfServices(false);
177
mLastCommitStatus = false;
178
}
179
return true;
180
}
先看看关于DefaultAidRouteResolveCache类内的一些方法
1
//calculateAidRouteSize详解.
2
public int calculateAidRouteSize(Hashtable<String, AidElement> routeCache) {
3
int routeTableSize = 0x00;
4
int routeAidCount = 0x00;
5
for(Map.Entry<String, AidElement> aidEntry : routeCache.entrySet()) {
6
String aid = aidEntry.getKey();
7
if(aid.endsWith("*")) {
8
routeTableSize += ((aid.length() - 0x01) / 0x02) + AID_HDR_LENGTH; // removing prefix length
9
} else {
10
//例如当routeCache有50个aid,并且每个aid是16byte,那么有如下:
11
//32/2+4 = 20 循环50次 20*50 = 1000,很明显超过大小了
12
routeTableSize += (aid.length() / 0x02)+ AID_HDR_LENGTH;
13
}
14
routeAidCount++;
15
}
16
//pn553 is 800 mAidRoutingTableSize
17
if(routeTableSize <= mAidRoutingTableSize && routeAidCount > MAX_AID_ENTRIES) {
18
routeTableSize = mAidRoutingTableSize + 0x01;
19
}
20
//把计算好的size返回回去
21
return routeTableSize;
22
}
1
//updateDefaultAidRouteCache详解
2
public int updateDefaultAidRouteCache(Hashtable<String, AidElement> routeCache , int route) {
3
int routesize = 0x00;
4
Hashtable<String, AidElement> tempRouteCache = new Hashtable<String, AidElement>(0x50);
5
//把传入的routeCache存到当前类下.
6
tempRouteCache.putAll(routeCache);
7
//计算出大小
8
routesize = calculateAidRouteSize(tempRouteCache);
9
//保存route id和对应的routeCache
10
aidCacheTable.put(route, tempRouteCache);
11
//保存route id和对应的cacheSize
12
aidCacheSize.put(route, routesize);
13
Log.d(TAG, "updateDefaultAidRouteCache Routing table size" +routesize);
14
return routesize;
15
}
//getNextRouteLoc详解
1
public int getNextRouteLoc() {
2
3
//同样是遍历0~3,
4
for (int i = 0; i < 0x03; i++) {
5
//当不是default的route,并且没有添加到aidRoutes里面过(也就是第一次碰见),
6
//直接返回这个route索引.
7
if((i != mCurrDefaultAidRoute) && (!aidRoutes.contains(i)))
8
{
9
aidRoutes.add(i);
10
return i;
11
}
12
}
13
return 0xFF;
14
}
1
//resolveDefaultAidRoute详解
2
public boolean resolveDefaultAidRoute () {
3
4
int minRoute = 0xFF;
5
int minAidRouteSize = mAidRoutingTableSize;
6
int tempRoute = 0x00;
7
int tempCacheSize = 0x00;
8
Hashtable<String, AidElement> aidRouteCache = new Hashtable<String, AidElement>();
9
Set<Integer> keys = aidCacheSize.keySet();
10
//遍历每个route的size.
11
for (Integer key : keys) {
12
tempRoute = key;
13
tempCacheSize = aidCacheSize.get(key);
14
//当是Host overflow的时候
15
//09-20 03:37:16.555 D/zy ( 2748): key = 0,tempCacheSize = 40
16
//09-20 03:37:16.555 D/zy ( 2748): key = 1,tempCacheSize = 1000
17
//09-20 03:37:16.555 D/zy ( 2748): key = 2,tempCacheSize = 960
18
19
//当是UICC overflow的时候
20
//09-20 03:55:20.359 D/zy ( 2748): key = 0,tempCacheSize = 1000
21
//09-20 03:55:20.359 D/zy ( 2748): key = 1,tempCacheSize = 1000
22
//09-20 03:55:20.359 D/zy ( 2748): key = 2,tempCacheSize = 0
23
24
//当全部都overflow的时候
25
//09-20 04:19:57.558 D/zy ( 2748): key = 0,tempCacheSize = 1000
26
//09-20 04:19:57.558 D/zy ( 2748): key = 1,tempCacheSize = 2000
27
//09-20 04:19:57.558 D/zy ( 2748): key = 2,tempCacheSize = 1000
28
29
//比较算出较小的CacheSize,先是跟最大限制比较.
30
if (tempCacheSize <= minAidRouteSize) {
31
minAidRouteSize = tempCacheSize;
32
//拿到较小的route
33
minRoute = tempRoute;
34
}
35
}
36
//当有一个没有overflow的时候
37
if(minRoute != 0xFF) {
38
mAidRouteResolvedStatus = true;
39
mResolvedAidRoute = minRoute;
40
}
41
return mAidRouteResolvedStatus;
42
}
1
//commit详解
2
private void commit(Hashtable<String, AidElement> routeCache ) {
3
if(routeCache != null) {
4
List<AidElement> list = Collections.list(routeCache.elements());
5
Collections.sort(list);
6
Iterator<AidElement> it = list.iterator();
7
NfcService.getInstance().clearRouting();
8
while(it.hasNext()){
9
AidElement element =it.next();
10
if (DBG) Log.d (TAG, element.toString());
11
//如下最后调用到NativeNfcManager.java中的doRouteAid
12
//public native boolean doRouteAid(byte[] aid, int route, int powerState, boolean isprefix);
13
NfcService.getInstance().routeAids(
14
element.getAid(),
15
element.getRouteLocation(),
16
element.getPowerState()
17
);
18
}
19
}
20
// And finally commit the routing
21
NfcService.getInstance().commitRouting();
22
}
23
24