1a1ec95882901ebf386cc794122b36b18ee845554Martijn Coenen/*
2a1ec95882901ebf386cc794122b36b18ee845554Martijn Coenen * Copyright (C) 2015 The Android Open Source Project
3a1ec95882901ebf386cc794122b36b18ee845554Martijn Coenen *
4a1ec95882901ebf386cc794122b36b18ee845554Martijn Coenen * Licensed under the Apache License, Version 2.0 (the "License");
5a1ec95882901ebf386cc794122b36b18ee845554Martijn Coenen * you may not use this file except in compliance with the License.
6a1ec95882901ebf386cc794122b36b18ee845554Martijn Coenen * You may obtain a copy of the License at
7a1ec95882901ebf386cc794122b36b18ee845554Martijn Coenen *
8a1ec95882901ebf386cc794122b36b18ee845554Martijn Coenen *      http://www.apache.org/licenses/LICENSE-2.0
9a1ec95882901ebf386cc794122b36b18ee845554Martijn Coenen *
10a1ec95882901ebf386cc794122b36b18ee845554Martijn Coenen * Unless required by applicable law or agreed to in writing, software
11a1ec95882901ebf386cc794122b36b18ee845554Martijn Coenen * distributed under the License is distributed on an "AS IS" BASIS,
12a1ec95882901ebf386cc794122b36b18ee845554Martijn Coenen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13a1ec95882901ebf386cc794122b36b18ee845554Martijn Coenen * See the License for the specific language governing permissions and
14a1ec95882901ebf386cc794122b36b18ee845554Martijn Coenen * limitations under the License.
15a1ec95882901ebf386cc794122b36b18ee845554Martijn Coenen */
16a1ec95882901ebf386cc794122b36b18ee845554Martijn Coenen
17aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenenpackage android.nfc.cardemulation;
18aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen
19aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenenimport java.io.IOException;
20aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenenimport java.util.ArrayList;
21df48db3c7cab1e39ffe16738c070644c1ef66782Martijn Coenenimport java.util.List;
22aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen
23aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenenimport org.xmlpull.v1.XmlPullParser;
24aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenenimport org.xmlpull.v1.XmlPullParserException;
25aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenenimport org.xmlpull.v1.XmlSerializer;
26aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen
27aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenenimport android.os.Parcel;
28aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenenimport android.os.Parcelable;
29aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenenimport android.util.Log;
30aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen
31aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen/**
322f6f3a0181b008f58b18804b749d5ddf1ba73bc8Martijn Coenen * The AidGroup class represents a group of Application Identifiers (AIDs).
332f6f3a0181b008f58b18804b749d5ddf1ba73bc8Martijn Coenen *
342f6f3a0181b008f58b18804b749d5ddf1ba73bc8Martijn Coenen * <p>The format of AIDs is defined in the ISO/IEC 7816-4 specification. This class
352f6f3a0181b008f58b18804b749d5ddf1ba73bc8Martijn Coenen * requires the AIDs to be input as a hexadecimal string, with an even amount of
362f6f3a0181b008f58b18804b749d5ddf1ba73bc8Martijn Coenen * hexadecimal characters, e.g. "F014811481".
37df48db3c7cab1e39ffe16738c070644c1ef66782Martijn Coenen *
38df48db3c7cab1e39ffe16738c070644c1ef66782Martijn Coenen * @hide
39aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen */
40aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenenpublic final class AidGroup implements Parcelable {
41aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    /**
42aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen     * The maximum number of AIDs that can be present in any one group.
43aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen     */
44aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    public static final int MAX_NUM_AIDS = 256;
45aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen
46aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    static final String TAG = "AidGroup";
47aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen
48df48db3c7cab1e39ffe16738c070644c1ef66782Martijn Coenen    final List<String> aids;
49aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    final String category;
50aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    final String description;
51aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen
52aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    /**
53aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen     * Creates a new AidGroup object.
54aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen     *
55aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen     * @param aids The list of AIDs present in the group
562f6f3a0181b008f58b18804b749d5ddf1ba73bc8Martijn Coenen     * @param category The category of this group, e.g. {@link CardEmulation#CATEGORY_PAYMENT}
57aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen     */
58df48db3c7cab1e39ffe16738c070644c1ef66782Martijn Coenen    public AidGroup(List<String> aids, String category) {
59aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        if (aids == null || aids.size() == 0) {
60aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen            throw new IllegalArgumentException("No AIDS in AID group.");
61aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        }
62aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        if (aids.size() > MAX_NUM_AIDS) {
63aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen            throw new IllegalArgumentException("Too many AIDs in AID group.");
64aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        }
651bfc3d624925c2b6e0928e26cf0660aee0a05ddfMartijn Coenen        for (String aid : aids) {
66b51441163fc4c493cddce08f5418021a31d0a719Martijn Coenen            if (!CardEmulation.isValidAid(aid)) {
671bfc3d624925c2b6e0928e26cf0660aee0a05ddfMartijn Coenen                throw new IllegalArgumentException("AID " + aid + " is not a valid AID.");
681bfc3d624925c2b6e0928e26cf0660aee0a05ddfMartijn Coenen            }
691bfc3d624925c2b6e0928e26cf0660aee0a05ddfMartijn Coenen        }
702f6f3a0181b008f58b18804b749d5ddf1ba73bc8Martijn Coenen        if (isValidCategory(category)) {
712f6f3a0181b008f58b18804b749d5ddf1ba73bc8Martijn Coenen            this.category = category;
722f6f3a0181b008f58b18804b749d5ddf1ba73bc8Martijn Coenen        } else {
732f6f3a0181b008f58b18804b749d5ddf1ba73bc8Martijn Coenen            this.category = CardEmulation.CATEGORY_OTHER;
74aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        }
755e991a129c308805da25957d9f5bf1bab8bcb60dMartijn Coenen        this.aids = new ArrayList<String>(aids.size());
765e991a129c308805da25957d9f5bf1bab8bcb60dMartijn Coenen        for (String aid : aids) {
775e991a129c308805da25957d9f5bf1bab8bcb60dMartijn Coenen            this.aids.add(aid.toUpperCase());
785e991a129c308805da25957d9f5bf1bab8bcb60dMartijn Coenen        }
79aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        this.description = null;
80aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    }
81aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen
82aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    AidGroup(String category, String description) {
83aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        this.aids = new ArrayList<String>();
84aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        this.category = category;
85aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        this.description = description;
86aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    }
87aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen
88aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    /**
89aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen     * @return the category of this AID group
90aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen     */
91aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    public String getCategory() {
92aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        return category;
93aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    }
94aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen
95aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    /**
96826a73b83b7f05ff92e51a2880fb4a75de08a9d1Martijn Coenen     * @return the list of AIDs in this group
97aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen     */
98df48db3c7cab1e39ffe16738c070644c1ef66782Martijn Coenen    public List<String> getAids() {
99aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        return aids;
100aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    }
101aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen
102aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    @Override
103aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    public String toString() {
104aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        StringBuilder out = new StringBuilder("Category: " + category +
105aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen                  ", AIDs:");
106aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        for (String aid : aids) {
107aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen            out.append(aid);
108aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen            out.append(", ");
109aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        }
110aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        return out.toString();
111aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    }
112aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen
113aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    @Override
114aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    public int describeContents() {
115aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        return 0;
116aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    }
117aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen
118aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    @Override
119aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    public void writeToParcel(Parcel dest, int flags) {
120aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        dest.writeString(category);
121aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        dest.writeInt(aids.size());
122aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        if (aids.size() > 0) {
123aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen            dest.writeStringList(aids);
124aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        }
125aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    }
126aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen
127aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    public static final Parcelable.Creator<AidGroup> CREATOR =
128aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen            new Parcelable.Creator<AidGroup>() {
129aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen
130aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        @Override
131aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        public AidGroup createFromParcel(Parcel source) {
132aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen            String category = source.readString();
133aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen            int listSize = source.readInt();
134aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen            ArrayList<String> aidList = new ArrayList<String>();
135aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen            if (listSize > 0) {
136aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen                source.readStringList(aidList);
137aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen            }
138aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen            return new AidGroup(aidList, category);
139aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        }
140aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen
141aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        @Override
142aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        public AidGroup[] newArray(int size) {
143aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen            return new AidGroup[size];
144aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        }
145aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    };
146aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen
147aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    static public AidGroup createFromXml(XmlPullParser parser) throws XmlPullParserException, IOException {
148b92dc6b013f4637bc29b09b9360b37810771ad1bMartijn Coenen        String category = null;
149aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        ArrayList<String> aids = new ArrayList<String>();
150b92dc6b013f4637bc29b09b9360b37810771ad1bMartijn Coenen        AidGroup group = null;
151b92dc6b013f4637bc29b09b9360b37810771ad1bMartijn Coenen        boolean inGroup = false;
152b92dc6b013f4637bc29b09b9360b37810771ad1bMartijn Coenen
153aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        int eventType = parser.getEventType();
154aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        int minDepth = parser.getDepth();
155aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        while (eventType != XmlPullParser.END_DOCUMENT && parser.getDepth() >= minDepth) {
156b92dc6b013f4637bc29b09b9360b37810771ad1bMartijn Coenen            String tagName = parser.getName();
157aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen            if (eventType == XmlPullParser.START_TAG) {
158aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen                if (tagName.equals("aid")) {
159b92dc6b013f4637bc29b09b9360b37810771ad1bMartijn Coenen                    if (inGroup) {
160b92dc6b013f4637bc29b09b9360b37810771ad1bMartijn Coenen                        String aid = parser.getAttributeValue(null, "value");
161b92dc6b013f4637bc29b09b9360b37810771ad1bMartijn Coenen                        if (aid != null) {
1625e991a129c308805da25957d9f5bf1bab8bcb60dMartijn Coenen                            aids.add(aid.toUpperCase());
163b92dc6b013f4637bc29b09b9360b37810771ad1bMartijn Coenen                        }
164b92dc6b013f4637bc29b09b9360b37810771ad1bMartijn Coenen                    } else {
165b92dc6b013f4637bc29b09b9360b37810771ad1bMartijn Coenen                        Log.d(TAG, "Ignoring <aid> tag while not in group");
166b92dc6b013f4637bc29b09b9360b37810771ad1bMartijn Coenen                    }
167b92dc6b013f4637bc29b09b9360b37810771ad1bMartijn Coenen                } else if (tagName.equals("aid-group")) {
168b92dc6b013f4637bc29b09b9360b37810771ad1bMartijn Coenen                    category = parser.getAttributeValue(null, "category");
169b92dc6b013f4637bc29b09b9360b37810771ad1bMartijn Coenen                    if (category == null) {
170b92dc6b013f4637bc29b09b9360b37810771ad1bMartijn Coenen                        Log.e(TAG, "<aid-group> tag without valid category");
171b92dc6b013f4637bc29b09b9360b37810771ad1bMartijn Coenen                        return null;
172aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen                    }
173b92dc6b013f4637bc29b09b9360b37810771ad1bMartijn Coenen                    inGroup = true;
174aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen                } else {
175b92dc6b013f4637bc29b09b9360b37810771ad1bMartijn Coenen                    Log.d(TAG, "Ignoring unexpected tag: " + tagName);
176b92dc6b013f4637bc29b09b9360b37810771ad1bMartijn Coenen                }
177b92dc6b013f4637bc29b09b9360b37810771ad1bMartijn Coenen            } else if (eventType == XmlPullParser.END_TAG) {
178b92dc6b013f4637bc29b09b9360b37810771ad1bMartijn Coenen                if (tagName.equals("aid-group") && inGroup && aids.size() > 0) {
179b92dc6b013f4637bc29b09b9360b37810771ad1bMartijn Coenen                    group = new AidGroup(aids, category);
180b92dc6b013f4637bc29b09b9360b37810771ad1bMartijn Coenen                    break;
181aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen                }
182aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen            }
183aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen            eventType = parser.next();
184aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        }
185b92dc6b013f4637bc29b09b9360b37810771ad1bMartijn Coenen        return group;
186aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    }
187aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen
188aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    public void writeAsXml(XmlSerializer out) throws IOException {
189b92dc6b013f4637bc29b09b9360b37810771ad1bMartijn Coenen        out.startTag(null, "aid-group");
190aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        out.attribute(null, "category", category);
191aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        for (String aid : aids) {
192aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen            out.startTag(null, "aid");
193aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen            out.attribute(null, "value", aid);
194aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen            out.endTag(null, "aid");
195aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        }
196b92dc6b013f4637bc29b09b9360b37810771ad1bMartijn Coenen        out.endTag(null, "aid-group");
197aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    }
198aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen
1992f6f3a0181b008f58b18804b749d5ddf1ba73bc8Martijn Coenen    static boolean isValidCategory(String category) {
200aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen        return CardEmulation.CATEGORY_PAYMENT.equals(category) ||
201aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen                CardEmulation.CATEGORY_OTHER.equals(category);
202aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen    }
203aa1492d1d8c5f80e074faacb83905bd07487975dMartijn Coenen}
204