Nfc源码分析之route相关

       关于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
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 安卓读取身份证的源码可以使用安卓提供的NFC(Near Field Communication)技术来实现。下面是一个简单的示例代码: 1. 配置AndroidManifest.xml文件,添加以下权限: ```xml <uses-permission android:name="android.permission.NFC" /> <uses-feature android:name="android.hardware.nfc" android:required="true" /> ``` 2. 在Activity中创建一个NfcAdapter对象,并在onCreate方法中初始化NFC适配器: ```java private NfcAdapter mNfcAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mNfcAdapter = NfcAdapter.getDefaultAdapter(this); } ``` 3. override onResume和onPause方法,分别在Activity可见时启用NFC监听,不可见时禁用NFC监听: ```java @Override protected void onResume() { super.onResume(); Intent intent = new Intent(this, getClass()); intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0); IntentFilter[] intentFilters = new IntentFilter[]{}; mNfcAdapter.enableForegroundDispatch(this, pendingIntent, intentFilters, null); } @Override protected void onPause() { super.onPause(); mNfcAdapter.disableForegroundDispatch(this); } ``` 4. 在onNewIntent方法中使用Tag对象获取身份证卡的数据: ```java @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())) { Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); // 使用Tag对象读取身份证数据 // ... } } ``` 这是一个简单的示例代码,实际读取身份证的过程需要使用ISO 14443-4协议进行通信,并对接收的数据进行解析。具体的实现需要参考相关的身份证阅读器设备API和协议文档。 ### 回答2: 安卓 NFC 读取身份证的实现源码如下所示: ```java import android.app.Activity; import android.app.PendingIntent; import android.content.Intent; import android.content.IntentFilter; import android.nfc.NfcAdapter; import android.nfc.Tag; import android.nfc.tech.IsoDep; import android.os.Bundle; public class MainActivity extends Activity { private NfcAdapter mNfcAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 获取 NFC 适配器 mNfcAdapter = NfcAdapter.getDefaultAdapter(this); if (mNfcAdapter == null) { // 设备不支持 NFC return; } // 创建一个 PendingIntent 对象,用于将其传递给 NFC 读卡器的操作 PendingIntent pendingIntent = PendingIntent.getActivity( this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0); // 创建 Intent 过滤器,当检测到 NFC 标签时过滤当前 Activity 的 Intent IntentFilter[] intentFilters = new IntentFilter[]{ new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED), }; // 创建技术列表数组 String[][] techListsArray = new String[][]{ new String[]{IsoDep.class.getName()} }; // 启用前台调度系统以将 NFC 事件发送到指定的 Activity mNfcAdapter.enableForegroundDispatch(this, pendingIntent, intentFilters, techListsArray); } @Override protected void onResume() { super.onResume(); if (mNfcAdapter != null) { // 启用前台调度系统 mNfcAdapter.enableForegroundDispatch(this, mPendingIntent, null, null); } } @Override protected void onPause() { super.onPause(); if (mNfcAdapter != null) { // 禁用前台调度系统 mNfcAdapter.disableForegroundDispatch(this); } } @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); // 处理获取的 NFC 标签 Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); // 连接到 NFC 标签 IsoDep isoDep = IsoDep.get(tag); if (isoDep != null) { try { // 打开连接 isoDep.connect(); // 读取数据 byte[] result = isoDep.transceive(new byte[]{}); // 添加具体的指令来读取身份证信息 // 处理读取到的数据 // TODO: 处理身份证数据 } catch (Exception e) { e.printStackTrace(); } finally { try { // 断开连接 isoDep.close(); } catch (Exception e) { e.printStackTrace(); } } } } } ``` 上述代码是一个简单的 Android NFC 读取身份证源码示例,你可以在其中添加具体的指令来读取身份证信息,并处理读取到的数据。注意,需要在 AndroidManifest.xml 文件中添加相应的权限和 NFC 相关配置信息。 同时,请注意,该源码是一个基本实现示例,具体实现可能因不同的设备和卡片而有所差异。在实际使用中,建议参考相关的官方文档和以往的经验进行开发。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值