1aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenenpackage android.nfc.cardemulation;
2aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen
3aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenenimport java.io.IOException;
4aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenenimport java.util.ArrayList;
5df48db3c7cab1e39ffe16738c070644c1ef66782Martijn Coenenimport java.util.List;
6aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen
7aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenenimport org.xmlpull.v1.XmlPullParser;
8aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenenimport org.xmlpull.v1.XmlPullParserException;
9aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenenimport org.xmlpull.v1.XmlSerializer;
10aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen
11aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenenimport android.os.Parcel;
12aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenenimport android.os.Parcelable;
13aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenenimport android.util.Log;
14aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen
15aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen/**
162f6f3a0181b008f58b18804b749d5ddf1ba73bc8Martijn Coenen * The AidGroup class represents a group of Application Identifiers (AIDs).
172f6f3a0181b008f58b18804b749d5ddf1ba73bc8Martijn Coenen *
182f6f3a0181b008f58b18804b749d5ddf1ba73bc8Martijn Coenen * <p>The format of AIDs is defined in the ISO/IEC 7816-4 specification. This class
192f6f3a0181b008f58b18804b749d5ddf1ba73bc8Martijn Coenen * requires the AIDs to be input as a hexadecimal string, with an even amount of
202f6f3a0181b008f58b18804b749d5ddf1ba73bc8Martijn Coenen * hexadecimal characters, e.g. "F014811481".
21df48db3c7cab1e39ffe16738c070644c1ef66782Martijn Coenen *
22df48db3c7cab1e39ffe16738c070644c1ef66782Martijn Coenen * @hide
23aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen */
24aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenenpublic final class AidGroup implements Parcelable {
25aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    /**
26aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen     * The maximum number of AIDs that can be present in any one group.
27aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen     */
28aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    public static final int MAX_NUM_AIDS = 256;
29aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen
30aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    static final String TAG = "AidGroup";
31aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen
32df48db3c7cab1e39ffe16738c070644c1ef66782Martijn Coenen    final List<String> aids;
33aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    final String category;
34aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    final String description;
35aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen
36aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    /**
37aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen     * Creates a new AidGroup object.
38aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen     *
39aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen     * @param aids The list of AIDs present in the group
402f6f3a0181b008f58b18804b749d5ddf1ba73bc8Martijn Coenen     * @param category The category of this group, e.g. {@link CardEmulation#CATEGORY_PAYMENT}
41aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen     */
42df48db3c7cab1e39ffe16738c070644c1ef66782Martijn Coenen    public AidGroup(List<String> aids, String category) {
43aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        if (aids == null || aids.size() == 0) {
44aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen            throw new IllegalArgumentException("No AIDS in AID group.");
45aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        }
46aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        if (aids.size() > MAX_NUM_AIDS) {
47aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen            throw new IllegalArgumentException("Too many AIDs in AID group.");
48aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        }
491bfc3d624925c2b6e0928e26cf0660aee0a05ddfMartijn Coenen        for (String aid : aids) {
50b51441163fc4c493cddce08f5418021a31d0a719Martijn Coenen            if (!CardEmulation.isValidAid(aid)) {
511bfc3d624925c2b6e0928e26cf0660aee0a05ddfMartijn Coenen                throw new IllegalArgumentException("AID " + aid + " is not a valid AID.");
521bfc3d624925c2b6e0928e26cf0660aee0a05ddfMartijn Coenen            }
531bfc3d624925c2b6e0928e26cf0660aee0a05ddfMartijn Coenen        }
542f6f3a0181b008f58b18804b749d5ddf1ba73bc8Martijn Coenen        if (isValidCategory(category)) {
552f6f3a0181b008f58b18804b749d5ddf1ba73bc8Martijn Coenen            this.category = category;
562f6f3a0181b008f58b18804b749d5ddf1ba73bc8Martijn Coenen        } else {
572f6f3a0181b008f58b18804b749d5ddf1ba73bc8Martijn Coenen            this.category = CardEmulation.CATEGORY_OTHER;
58aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        }
595e991a129c308805da25957d9f5bf1bab8bcb60dMartijn Coenen        this.aids = new ArrayList<String>(aids.size());
605e991a129c308805da25957d9f5bf1bab8bcb60dMartijn Coenen        for (String aid : aids) {
615e991a129c308805da25957d9f5bf1bab8bcb60dMartijn Coenen            this.aids.add(aid.toUpperCase());
625e991a129c308805da25957d9f5bf1bab8bcb60dMartijn Coenen        }
63aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        this.description = null;
64aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    }
65aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen
66aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    AidGroup(String category, String description) {
67aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        this.aids = new ArrayList<String>();
68aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        this.category = category;
69aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        this.description = description;
70aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    }
71aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen
72aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    /**
73aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen     * @return the category of this AID group
74aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen     */
75aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    public String getCategory() {
76aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        return category;
77aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    }
78aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen
79aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    /**
80826a73b83b7f05ff92e51a2880fb4a75de08a9d1Martijn Coenen     * @return the list of AIDs in this group
81aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen     */
82df48db3c7cab1e39ffe16738c070644c1ef66782Martijn Coenen    public List<String> getAids() {
83aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        return aids;
84aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    }
85aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen
86aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    @Override
87aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    public String toString() {
88aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        StringBuilder out = new StringBuilder("Category: " + category +
89aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen                  ", AIDs:");
90aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        for (String aid : aids) {
91aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen            out.append(aid);
92aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen            out.append(", ");
93aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        }
94aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        return out.toString();
95aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    }
96aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen
97aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    @Override
98aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    public int describeContents() {
99aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        return 0;
100aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    }
101aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen
102aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    @Override
103aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    public void writeToParcel(Parcel dest, int flags) {
104aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        dest.writeString(category);
105aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        dest.writeInt(aids.size());
106aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        if (aids.size() > 0) {
107aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen            dest.writeStringList(aids);
108aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        }
109aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    }
110aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen
111aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    public static final Parcelable.Creator<AidGroup> CREATOR =
112aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen            new Parcelable.Creator<AidGroup>() {
113aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen
114aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        @Override
115aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        public AidGroup createFromParcel(Parcel source) {
116aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen            String category = source.readString();
117aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen            int listSize = source.readInt();
118aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen            ArrayList<String> aidList = new ArrayList<String>();
119aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen            if (listSize > 0) {
120aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen                source.readStringList(aidList);
121aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen            }
122aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen            return new AidGroup(aidList, category);
123aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        }
124aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen
125aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        @Override
126aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        public AidGroup[] newArray(int size) {
127aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen            return new AidGroup[size];
128aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        }
129aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    };
130aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen
131aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    static public AidGroup createFromXml(XmlPullParser parser) throws XmlPullParserException, IOException {
132b92dc6b013f4637bc29b09b9360b37810771ad1bMartijn Coenen        String category = null;
133aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        ArrayList<String> aids = new ArrayList<String>();
134b92dc6b013f4637bc29b09b9360b37810771ad1bMartijn Coenen        AidGroup group = null;
135b92dc6b013f4637bc29b09b9360b37810771ad1bMartijn Coenen        boolean inGroup = false;
136b92dc6b013f4637bc29b09b9360b37810771ad1bMartijn Coenen
137aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        int eventType = parser.getEventType();
138aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        int minDepth = parser.getDepth();
139aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        while (eventType != XmlPullParser.END_DOCUMENT && parser.getDepth() >= minDepth) {
140b92dc6b013f4637bc29b09b9360b37810771ad1bMartijn Coenen            String tagName = parser.getName();
141aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen            if (eventType == XmlPullParser.START_TAG) {
142aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen                if (tagName.equals("aid")) {
143b92dc6b013f4637bc29b09b9360b37810771ad1bMartijn Coenen                    if (inGroup) {
144b92dc6b013f4637bc29b09b9360b37810771ad1bMartijn Coenen                        String aid = parser.getAttributeValue(null, "value");
145b92dc6b013f4637bc29b09b9360b37810771ad1bMartijn Coenen                        if (aid != null) {
1465e991a129c308805da25957d9f5bf1bab8bcb60dMartijn Coenen                            aids.add(aid.toUpperCase());
147b92dc6b013f4637bc29b09b9360b37810771ad1bMartijn Coenen                        }
148b92dc6b013f4637bc29b09b9360b37810771ad1bMartijn Coenen                    } else {
149b92dc6b013f4637bc29b09b9360b37810771ad1bMartijn Coenen                        Log.d(TAG, "Ignoring <aid> tag while not in group");
150b92dc6b013f4637bc29b09b9360b37810771ad1bMartijn Coenen                    }
151b92dc6b013f4637bc29b09b9360b37810771ad1bMartijn Coenen                } else if (tagName.equals("aid-group")) {
152b92dc6b013f4637bc29b09b9360b37810771ad1bMartijn Coenen                    category = parser.getAttributeValue(null, "category");
153b92dc6b013f4637bc29b09b9360b37810771ad1bMartijn Coenen                    if (category == null) {
154b92dc6b013f4637bc29b09b9360b37810771ad1bMartijn Coenen                        Log.e(TAG, "<aid-group> tag without valid category");
155b92dc6b013f4637bc29b09b9360b37810771ad1bMartijn Coenen                        return null;
156aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen                    }
157b92dc6b013f4637bc29b09b9360b37810771ad1bMartijn Coenen                    inGroup = true;
158aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen                } else {
159b92dc6b013f4637bc29b09b9360b37810771ad1bMartijn Coenen                    Log.d(TAG, "Ignoring unexpected tag: " + tagName);
160b92dc6b013f4637bc29b09b9360b37810771ad1bMartijn Coenen                }
161b92dc6b013f4637bc29b09b9360b37810771ad1bMartijn Coenen            } else if (eventType == XmlPullParser.END_TAG) {
162b92dc6b013f4637bc29b09b9360b37810771ad1bMartijn Coenen                if (tagName.equals("aid-group") && inGroup && aids.size() > 0) {
163b92dc6b013f4637bc29b09b9360b37810771ad1bMartijn Coenen                    group = new AidGroup(aids, category);
164b92dc6b013f4637bc29b09b9360b37810771ad1bMartijn Coenen                    break;
165aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen                }
166aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen            }
167aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen            eventType = parser.next();
168aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        }
169b92dc6b013f4637bc29b09b9360b37810771ad1bMartijn Coenen        return group;
170aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    }
171aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen
172aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    public void writeAsXml(XmlSerializer out) throws IOException {
173b92dc6b013f4637bc29b09b9360b37810771ad1bMartijn Coenen        out.startTag(null, "aid-group");
174aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        out.attribute(null, "category", category);
175aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        for (String aid : aids) {
176aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen            out.startTag(null, "aid");
177aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen            out.attribute(null, "value", aid);
178aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen            out.endTag(null, "aid");
179aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        }
180b92dc6b013f4637bc29b09b9360b37810771ad1bMartijn Coenen        out.endTag(null, "aid-group");
181aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    }
182aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen
1832f6f3a0181b008f58b18804b749d5ddf1ba73bc8Martijn Coenen    static boolean isValidCategory(String category) {
184aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        return CardEmulation.CATEGORY_PAYMENT.equals(category) ||
185aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen                CardEmulation.CATEGORY_OTHER.equals(category);
186aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    }
187aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen}
188