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 android.annotation.Nullable;
20 import android.content.ComponentName;
21 import android.content.pm.PackageManager;
22 import android.content.pm.PackageManager.NameNotFoundException;
23 import android.content.pm.ResolveInfo;
24 import android.content.pm.ServiceInfo;
25 import android.content.res.Resources;
26 import android.content.res.TypedArray;
27 import android.content.res.XmlResourceParser;
28 import android.graphics.drawable.Drawable;
29 import android.os.Parcel;
30 import android.os.Parcelable;
31 import android.util.AttributeSet;
32 import android.util.Log;
33 import android.util.Xml;
34 import android.util.proto.ProtoOutputStream;
35 
36 import org.xmlpull.v1.XmlPullParser;
37 import org.xmlpull.v1.XmlPullParserException;
38 
39 import java.io.FileDescriptor;
40 import java.io.IOException;
41 import java.io.PrintWriter;
42 
43 /**
44  * @hide
45  */
46 public final class NfcFServiceInfo implements Parcelable {
47     static final String TAG = "NfcFServiceInfo";
48 
49     private static final String DEFAULT_T3T_PMM = "FFFFFFFFFFFFFFFF";
50 
51     /**
52      * The service that implements this
53      */
54     final ResolveInfo mService;
55 
56     /**
57      * Description of the service
58      */
59     final String mDescription;
60 
61     /**
62      * System Code of the service
63      */
64     final String mSystemCode;
65 
66     /**
67      * System Code of the service registered by API
68      */
69     String mDynamicSystemCode;
70 
71     /**
72      * NFCID2 of the service
73      */
74     final String mNfcid2;
75 
76     /**
77      * NFCID2 of the service registered by API
78      */
79     String mDynamicNfcid2;
80 
81     /**
82      * The uid of the package the service belongs to
83      */
84     final int mUid;
85 
86     /**
87      * LF_T3T_PMM of the service
88      */
89     final String mT3tPmm;
90 
91     /**
92      * @hide
93      */
NfcFServiceInfo(ResolveInfo info, String description, String systemCode, String dynamicSystemCode, String nfcid2, String dynamicNfcid2, int uid, String t3tPmm)94     public NfcFServiceInfo(ResolveInfo info, String description,
95             String systemCode, String dynamicSystemCode, String nfcid2, String dynamicNfcid2,
96             int uid, String t3tPmm) {
97         this.mService = info;
98         this.mDescription = description;
99         this.mSystemCode = systemCode;
100         this.mDynamicSystemCode = dynamicSystemCode;
101         this.mNfcid2 = nfcid2;
102         this.mDynamicNfcid2 = dynamicNfcid2;
103         this.mUid = uid;
104         this.mT3tPmm = t3tPmm;
105     }
106 
NfcFServiceInfo(PackageManager pm, ResolveInfo info)107     public NfcFServiceInfo(PackageManager pm, ResolveInfo info)
108             throws XmlPullParserException, IOException {
109         ServiceInfo si = info.serviceInfo;
110         XmlResourceParser parser = null;
111         try {
112             parser = si.loadXmlMetaData(pm, HostNfcFService.SERVICE_META_DATA);
113             if (parser == null) {
114                 throw new XmlPullParserException("No " + HostNfcFService.SERVICE_META_DATA +
115                         " meta-data");
116             }
117 
118             int eventType = parser.getEventType();
119             while (eventType != XmlPullParser.START_TAG &&
120                     eventType != XmlPullParser.END_DOCUMENT) {
121                 eventType = parser.next();
122             }
123 
124             String tagName = parser.getName();
125             if (!"host-nfcf-service".equals(tagName)) {
126                 throw new XmlPullParserException(
127                         "Meta-data does not start with <host-nfcf-service> tag");
128             }
129 
130             Resources res = pm.getResourcesForApplication(si.applicationInfo);
131             AttributeSet attrs = Xml.asAttributeSet(parser);
132             TypedArray sa = res.obtainAttributes(attrs,
133                     com.android.internal.R.styleable.HostNfcFService);
134             mService = info;
135             mDescription = sa.getString(
136                     com.android.internal.R.styleable.HostNfcFService_description);
137             mDynamicSystemCode = null;
138             mDynamicNfcid2 = null;
139             sa.recycle();
140 
141             String systemCode = null;
142             String nfcid2 = null;
143             String t3tPmm = null;
144             final int depth = parser.getDepth();
145 
146             while (((eventType = parser.next()) != XmlPullParser.END_TAG ||
147                     parser.getDepth() > depth) && eventType != XmlPullParser.END_DOCUMENT) {
148                 tagName = parser.getName();
149                 if (eventType == XmlPullParser.START_TAG &&
150                         "system-code-filter".equals(tagName) && systemCode == null) {
151                     final TypedArray a = res.obtainAttributes(attrs,
152                             com.android.internal.R.styleable.SystemCodeFilter);
153                     systemCode = a.getString(
154                             com.android.internal.R.styleable.SystemCodeFilter_name).toUpperCase();
155                     if (!NfcFCardEmulation.isValidSystemCode(systemCode) &&
156                             !systemCode.equalsIgnoreCase("NULL")) {
157                         Log.e(TAG, "Invalid System Code: " + systemCode);
158                         systemCode = null;
159                     }
160                     a.recycle();
161                 } else if (eventType == XmlPullParser.START_TAG &&
162                         "nfcid2-filter".equals(tagName) && nfcid2 == null) {
163                     final TypedArray a = res.obtainAttributes(attrs,
164                             com.android.internal.R.styleable.Nfcid2Filter);
165                     nfcid2 = a.getString(
166                             com.android.internal.R.styleable.Nfcid2Filter_name).toUpperCase();
167                     if (!nfcid2.equalsIgnoreCase("RANDOM") &&
168                             !nfcid2.equalsIgnoreCase("NULL") &&
169                             !NfcFCardEmulation.isValidNfcid2(nfcid2)) {
170                         Log.e(TAG, "Invalid NFCID2: " + nfcid2);
171                         nfcid2 = null;
172                     }
173                     a.recycle();
174                 } else if (eventType == XmlPullParser.START_TAG && tagName.equals("t3tPmm-filter")
175                         && t3tPmm == null) {
176                     final TypedArray a = res.obtainAttributes(attrs,
177                             com.android.internal.R.styleable.T3tPmmFilter);
178                     t3tPmm = a.getString(
179                             com.android.internal.R.styleable.T3tPmmFilter_name).toUpperCase();
180                     a.recycle();
181                 }
182             }
183             mSystemCode = (systemCode == null ? "NULL" : systemCode);
184             mNfcid2 = (nfcid2 == null ? "NULL" : nfcid2);
185             mT3tPmm = (t3tPmm == null ? DEFAULT_T3T_PMM : t3tPmm);
186         } catch (NameNotFoundException e) {
187             throw new XmlPullParserException("Unable to create context for: " + si.packageName);
188         } finally {
189             if (parser != null) parser.close();
190         }
191         // Set uid
192         mUid = si.applicationInfo.uid;
193     }
194 
getComponent()195     public ComponentName getComponent() {
196         return new ComponentName(mService.serviceInfo.packageName,
197                 mService.serviceInfo.name);
198     }
199 
getSystemCode()200     public String getSystemCode() {
201         return (mDynamicSystemCode == null ? mSystemCode : mDynamicSystemCode);
202     }
203 
setOrReplaceDynamicSystemCode(String systemCode)204     public void setOrReplaceDynamicSystemCode(String systemCode) {
205         mDynamicSystemCode = systemCode;
206     }
207 
getNfcid2()208     public String getNfcid2() {
209         return (mDynamicNfcid2 == null ? mNfcid2 : mDynamicNfcid2);
210     }
211 
setOrReplaceDynamicNfcid2(String nfcid2)212     public void setOrReplaceDynamicNfcid2(String nfcid2) {
213         mDynamicNfcid2 = nfcid2;
214     }
215 
getDescription()216     public String getDescription() {
217         return mDescription;
218     }
219 
getUid()220     public int getUid() {
221         return mUid;
222     }
223 
getT3tPmm()224     public String getT3tPmm() {
225         return mT3tPmm;
226     }
227 
loadLabel(PackageManager pm)228     public CharSequence loadLabel(PackageManager pm) {
229         return mService.loadLabel(pm);
230     }
231 
loadIcon(PackageManager pm)232     public Drawable loadIcon(PackageManager pm) {
233         return mService.loadIcon(pm);
234     }
235 
236     @Override
toString()237     public String toString() {
238         StringBuilder out = new StringBuilder("NfcFService: ");
239         out.append(getComponent());
240         out.append(", description: " + mDescription);
241         out.append(", System Code: " + mSystemCode);
242         if (mDynamicSystemCode != null) {
243             out.append(", dynamic System Code: " + mDynamicSystemCode);
244         }
245         out.append(", NFCID2: " + mNfcid2);
246         if (mDynamicNfcid2 != null) {
247             out.append(", dynamic NFCID2: " + mDynamicNfcid2);
248         }
249         out.append(", T3T PMM:" + mT3tPmm);
250         return out.toString();
251     }
252 
253     @Override
equals(@ullable Object o)254     public boolean equals(@Nullable Object o) {
255         if (this == o) return true;
256         if (!(o instanceof NfcFServiceInfo)) return false;
257         NfcFServiceInfo thatService = (NfcFServiceInfo) o;
258 
259         if (!thatService.getComponent().equals(this.getComponent())) return false;
260         if (!thatService.mSystemCode.equalsIgnoreCase(this.mSystemCode)) return false;
261         if (!thatService.mNfcid2.equalsIgnoreCase(this.mNfcid2)) return false;
262         if (!thatService.mT3tPmm.equalsIgnoreCase(this.mT3tPmm)) return false;
263         return true;
264     }
265 
266     @Override
hashCode()267     public int hashCode() {
268         return getComponent().hashCode();
269     }
270 
271     @Override
describeContents()272     public int describeContents() {
273         return 0;
274     }
275 
276     @Override
writeToParcel(Parcel dest, int flags)277     public void writeToParcel(Parcel dest, int flags) {
278         mService.writeToParcel(dest, flags);
279         dest.writeString(mDescription);
280         dest.writeString(mSystemCode);
281         dest.writeInt(mDynamicSystemCode != null ? 1 : 0);
282         if (mDynamicSystemCode != null) {
283             dest.writeString(mDynamicSystemCode);
284         }
285         dest.writeString(mNfcid2);
286         dest.writeInt(mDynamicNfcid2 != null ? 1 : 0);
287         if (mDynamicNfcid2 != null) {
288             dest.writeString(mDynamicNfcid2);
289         }
290         dest.writeInt(mUid);
291         dest.writeString(mT3tPmm);
292     };
293 
294     public static final @android.annotation.NonNull Parcelable.Creator<NfcFServiceInfo> CREATOR =
295             new Parcelable.Creator<NfcFServiceInfo>() {
296         @Override
297         public NfcFServiceInfo createFromParcel(Parcel source) {
298             ResolveInfo info = ResolveInfo.CREATOR.createFromParcel(source);
299             String description = source.readString();
300             String systemCode = source.readString();
301             String dynamicSystemCode = null;
302             if (source.readInt() != 0) {
303                 dynamicSystemCode = source.readString();
304             }
305             String nfcid2 = source.readString();
306             String dynamicNfcid2 = null;
307             if (source.readInt() != 0) {
308                 dynamicNfcid2 = source.readString();
309             }
310             int uid = source.readInt();
311             String t3tPmm = source.readString();
312             NfcFServiceInfo service = new NfcFServiceInfo(info, description,
313                     systemCode, dynamicSystemCode, nfcid2, dynamicNfcid2, uid, t3tPmm);
314             return service;
315         }
316 
317         @Override
318         public NfcFServiceInfo[] newArray(int size) {
319             return new NfcFServiceInfo[size];
320         }
321     };
322 
dump(FileDescriptor fd, PrintWriter pw, String[] args)323     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
324         pw.println("    " + getComponent() +
325                 " (Description: " + getDescription() + ")");
326         pw.println("    System Code: " + getSystemCode());
327         pw.println("    NFCID2: " + getNfcid2());
328         pw.println("    T3tPmm: " + getT3tPmm());
329     }
330 
331     /**
332      * Dump debugging info as NfcFServiceInfoProto
333      *
334      * If the output belongs to a sub message, the caller is responsible for wrapping this function
335      * between {@link ProtoOutputStream#start(long)} and {@link ProtoOutputStream#end(long)}.
336      *
337      * @param proto the ProtoOutputStream to write to
338      */
dumpDebug(ProtoOutputStream proto)339     public void dumpDebug(ProtoOutputStream proto) {
340         getComponent().dumpDebug(proto, NfcFServiceInfoProto.COMPONENT_NAME);
341         proto.write(NfcFServiceInfoProto.DESCRIPTION, getDescription());
342         proto.write(NfcFServiceInfoProto.SYSTEM_CODE, getSystemCode());
343         proto.write(NfcFServiceInfoProto.NFCID2, getNfcid2());
344         proto.write(NfcFServiceInfoProto.T3T_PMM, getT3tPmm());
345     }
346 }
347 
348