blob: 77b5552cd27b5ac6ce85fbf4916aed86e8c2a03d [file] [log] [blame]
Martijn Coenena1ec9582015-09-07 11:53:59 +02001/*
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
Martijn Coenenaa1492d2014-04-11 12:54:22 -070017package android.nfc.cardemulation;
18
19import java.io.IOException;
20import java.util.ArrayList;
Martijn Coenendf48db32014-05-20 13:52:14 -070021import java.util.List;
Martijn Coenenaa1492d2014-04-11 12:54:22 -070022
23import org.xmlpull.v1.XmlPullParser;
24import org.xmlpull.v1.XmlPullParserException;
25import org.xmlpull.v1.XmlSerializer;
26
Mathew Inwood57069dc2018-07-31 15:33:20 +010027import android.annotation.UnsupportedAppUsage;
Martijn Coenenaa1492d2014-04-11 12:54:22 -070028import android.os.Parcel;
29import android.os.Parcelable;
30import android.util.Log;
31
32/**
Martijn Coenen2f6f3a012014-04-25 17:00:21 -070033 * The AidGroup class represents a group of Application Identifiers (AIDs).
34 *
Martijn Coenen2f6f3a012014-04-25 17:00:21 -070035 * <p>The format of AIDs is defined in the ISO/IEC 7816-4 specification. This class
36 * requires the AIDs to be input as a hexadecimal string, with an even amount of
37 * hexadecimal characters, e.g. "F014811481".
Martijn Coenendf48db32014-05-20 13:52:14 -070038 *
39 * @hide
Martijn Coenenaa1492d2014-04-11 12:54:22 -070040 */
41public final class AidGroup implements Parcelable {
42 /**
43 * The maximum number of AIDs that can be present in any one group.
44 */
45 public static final int MAX_NUM_AIDS = 256;
46
47 static final String TAG = "AidGroup";
48
Mathew Inwood57069dc2018-07-31 15:33:20 +010049 @UnsupportedAppUsage
Martijn Coenendf48db32014-05-20 13:52:14 -070050 final List<String> aids;
Mathew Inwood57069dc2018-07-31 15:33:20 +010051 @UnsupportedAppUsage
Martijn Coenenaa1492d2014-04-11 12:54:22 -070052 final String category;
Mathew Inwood57069dc2018-07-31 15:33:20 +010053 @UnsupportedAppUsage
Martijn Coenenaa1492d2014-04-11 12:54:22 -070054 final String description;
55
56 /**
57 * Creates a new AidGroup object.
58 *
59 * @param aids The list of AIDs present in the group
Martijn Coenen2f6f3a012014-04-25 17:00:21 -070060 * @param category The category of this group, e.g. {@link CardEmulation#CATEGORY_PAYMENT}
Martijn Coenenaa1492d2014-04-11 12:54:22 -070061 */
Martijn Coenendf48db32014-05-20 13:52:14 -070062 public AidGroup(List<String> aids, String category) {
Martijn Coenenaa1492d2014-04-11 12:54:22 -070063 if (aids == null || aids.size() == 0) {
64 throw new IllegalArgumentException("No AIDS in AID group.");
65 }
66 if (aids.size() > MAX_NUM_AIDS) {
67 throw new IllegalArgumentException("Too many AIDs in AID group.");
68 }
Martijn Coenen1bfc3d62014-06-27 13:23:41 -070069 for (String aid : aids) {
Martijn Coenenb5144112014-07-02 12:44:33 -070070 if (!CardEmulation.isValidAid(aid)) {
Martijn Coenen1bfc3d62014-06-27 13:23:41 -070071 throw new IllegalArgumentException("AID " + aid + " is not a valid AID.");
72 }
73 }
Martijn Coenen2f6f3a012014-04-25 17:00:21 -070074 if (isValidCategory(category)) {
75 this.category = category;
76 } else {
77 this.category = CardEmulation.CATEGORY_OTHER;
Martijn Coenenaa1492d2014-04-11 12:54:22 -070078 }
Martijn Coenen5e991a12014-09-16 16:03:28 -070079 this.aids = new ArrayList<String>(aids.size());
80 for (String aid : aids) {
81 this.aids.add(aid.toUpperCase());
82 }
Martijn Coenenaa1492d2014-04-11 12:54:22 -070083 this.description = null;
84 }
85
Mathew Inwood57069dc2018-07-31 15:33:20 +010086 @UnsupportedAppUsage
Martijn Coenenaa1492d2014-04-11 12:54:22 -070087 AidGroup(String category, String description) {
88 this.aids = new ArrayList<String>();
89 this.category = category;
90 this.description = description;
91 }
92
93 /**
94 * @return the category of this AID group
95 */
Mathew Inwood57069dc2018-07-31 15:33:20 +010096 @UnsupportedAppUsage
Martijn Coenenaa1492d2014-04-11 12:54:22 -070097 public String getCategory() {
98 return category;
99 }
100
101 /**
Martijn Coenen826a73b2014-08-05 07:51:58 -0700102 * @return the list of AIDs in this group
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700103 */
Mathew Inwood57069dc2018-07-31 15:33:20 +0100104 @UnsupportedAppUsage
Martijn Coenendf48db32014-05-20 13:52:14 -0700105 public List<String> getAids() {
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700106 return aids;
107 }
108
109 @Override
110 public String toString() {
111 StringBuilder out = new StringBuilder("Category: " + category +
112 ", AIDs:");
113 for (String aid : aids) {
114 out.append(aid);
115 out.append(", ");
116 }
117 return out.toString();
118 }
119
120 @Override
121 public int describeContents() {
122 return 0;
123 }
124
125 @Override
126 public void writeToParcel(Parcel dest, int flags) {
127 dest.writeString(category);
128 dest.writeInt(aids.size());
129 if (aids.size() > 0) {
130 dest.writeStringList(aids);
131 }
132 }
133
Mathew Inwood57069dc2018-07-31 15:33:20 +0100134 @UnsupportedAppUsage
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -0700135 public static final @android.annotation.NonNull Parcelable.Creator<AidGroup> CREATOR =
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700136 new Parcelable.Creator<AidGroup>() {
137
138 @Override
139 public AidGroup createFromParcel(Parcel source) {
140 String category = source.readString();
141 int listSize = source.readInt();
142 ArrayList<String> aidList = new ArrayList<String>();
143 if (listSize > 0) {
144 source.readStringList(aidList);
145 }
146 return new AidGroup(aidList, category);
147 }
148
149 @Override
150 public AidGroup[] newArray(int size) {
151 return new AidGroup[size];
152 }
153 };
154
Mathew Inwood57069dc2018-07-31 15:33:20 +0100155 @UnsupportedAppUsage
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700156 static public AidGroup createFromXml(XmlPullParser parser) throws XmlPullParserException, IOException {
Martijn Coenenb92dc6b2014-07-02 13:58:42 -0700157 String category = null;
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700158 ArrayList<String> aids = new ArrayList<String>();
Martijn Coenenb92dc6b2014-07-02 13:58:42 -0700159 AidGroup group = null;
160 boolean inGroup = false;
161
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700162 int eventType = parser.getEventType();
163 int minDepth = parser.getDepth();
164 while (eventType != XmlPullParser.END_DOCUMENT && parser.getDepth() >= minDepth) {
Martijn Coenenb92dc6b2014-07-02 13:58:42 -0700165 String tagName = parser.getName();
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700166 if (eventType == XmlPullParser.START_TAG) {
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700167 if (tagName.equals("aid")) {
Martijn Coenenb92dc6b2014-07-02 13:58:42 -0700168 if (inGroup) {
169 String aid = parser.getAttributeValue(null, "value");
170 if (aid != null) {
Martijn Coenen5e991a12014-09-16 16:03:28 -0700171 aids.add(aid.toUpperCase());
Martijn Coenenb92dc6b2014-07-02 13:58:42 -0700172 }
173 } else {
174 Log.d(TAG, "Ignoring <aid> tag while not in group");
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700175 }
Martijn Coenenb92dc6b2014-07-02 13:58:42 -0700176 } else if (tagName.equals("aid-group")) {
177 category = parser.getAttributeValue(null, "category");
178 if (category == null) {
179 Log.e(TAG, "<aid-group> tag without valid category");
180 return null;
181 }
182 inGroup = true;
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700183 } else {
Martijn Coenenb92dc6b2014-07-02 13:58:42 -0700184 Log.d(TAG, "Ignoring unexpected tag: " + tagName);
185 }
186 } else if (eventType == XmlPullParser.END_TAG) {
187 if (tagName.equals("aid-group") && inGroup && aids.size() > 0) {
188 group = new AidGroup(aids, category);
189 break;
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700190 }
191 }
192 eventType = parser.next();
193 }
Martijn Coenenb92dc6b2014-07-02 13:58:42 -0700194 return group;
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700195 }
196
Mathew Inwood57069dc2018-07-31 15:33:20 +0100197 @UnsupportedAppUsage
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700198 public void writeAsXml(XmlSerializer out) throws IOException {
Martijn Coenenb92dc6b2014-07-02 13:58:42 -0700199 out.startTag(null, "aid-group");
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700200 out.attribute(null, "category", category);
201 for (String aid : aids) {
202 out.startTag(null, "aid");
203 out.attribute(null, "value", aid);
204 out.endTag(null, "aid");
205 }
Martijn Coenenb92dc6b2014-07-02 13:58:42 -0700206 out.endTag(null, "aid-group");
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700207 }
208
Martijn Coenen2f6f3a012014-04-25 17:00:21 -0700209 static boolean isValidCategory(String category) {
Martijn Coenenaa1492d2014-04-11 12:54:22 -0700210 return CardEmulation.CATEGORY_PAYMENT.equals(category) ||
211 CardEmulation.CATEGORY_OTHER.equals(category);
212 }
213}