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