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