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