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