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