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(", UID: " + mUid);
241         out.append(", description: " + mDescription);
242         out.append(", System Code: " + mSystemCode);
243         if (mDynamicSystemCode != null) {
244             out.append(", dynamic System Code: " + mDynamicSystemCode);
245         }
246         out.append(", NFCID2: " + mNfcid2);
247         if (mDynamicNfcid2 != null) {
248             out.append(", dynamic NFCID2: " + mDynamicNfcid2);
249         }
250         out.append(", T3T PMM:" + mT3tPmm);
251         return out.toString();
252     }
253 
254     @Override
equals(@ullable Object o)255     public boolean equals(@Nullable Object o) {
256         if (this == o) return true;
257         if (!(o instanceof NfcFServiceInfo)) return false;
258         NfcFServiceInfo thatService = (NfcFServiceInfo) o;
259 
260         if (!thatService.getComponent().equals(this.getComponent())) return false;
261         if (thatService.getUid() != this.getUid()) return false;
262         if (!thatService.mSystemCode.equalsIgnoreCase(this.mSystemCode)) return false;
263         if (!thatService.mNfcid2.equalsIgnoreCase(this.mNfcid2)) return false;
264         if (!thatService.mT3tPmm.equalsIgnoreCase(this.mT3tPmm)) return false;
265         return true;
266     }
267 
268     @Override
hashCode()269     public int hashCode() {
270         return getComponent().hashCode();
271     }
272 
273     @Override
describeContents()274     public int describeContents() {
275         return 0;
276     }
277 
278     @Override
writeToParcel(Parcel dest, int flags)279     public void writeToParcel(Parcel dest, int flags) {
280         mService.writeToParcel(dest, flags);
281         dest.writeString(mDescription);
282         dest.writeString(mSystemCode);
283         dest.writeInt(mDynamicSystemCode != null ? 1 : 0);
284         if (mDynamicSystemCode != null) {
285             dest.writeString(mDynamicSystemCode);
286         }
287         dest.writeString(mNfcid2);
288         dest.writeInt(mDynamicNfcid2 != null ? 1 : 0);
289         if (mDynamicNfcid2 != null) {
290             dest.writeString(mDynamicNfcid2);
291         }
292         dest.writeInt(mUid);
293         dest.writeString(mT3tPmm);
294     };
295 
296     public static final @android.annotation.NonNull Parcelable.Creator<NfcFServiceInfo> CREATOR =
297             new Parcelable.Creator<NfcFServiceInfo>() {
298         @Override
299         public NfcFServiceInfo createFromParcel(Parcel source) {
300             ResolveInfo info = ResolveInfo.CREATOR.createFromParcel(source);
301             String description = source.readString();
302             String systemCode = source.readString();
303             String dynamicSystemCode = null;
304             if (source.readInt() != 0) {
305                 dynamicSystemCode = source.readString();
306             }
307             String nfcid2 = source.readString();
308             String dynamicNfcid2 = null;
309             if (source.readInt() != 0) {
310                 dynamicNfcid2 = source.readString();
311             }
312             int uid = source.readInt();
313             String t3tPmm = source.readString();
314             NfcFServiceInfo service = new NfcFServiceInfo(info, description,
315                     systemCode, dynamicSystemCode, nfcid2, dynamicNfcid2, uid, t3tPmm);
316             return service;
317         }
318 
319         @Override
320         public NfcFServiceInfo[] newArray(int size) {
321             return new NfcFServiceInfo[size];
322         }
323     };
324 
dump(FileDescriptor fd, PrintWriter pw, String[] args)325     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
326         pw.println("    " + getComponent()
327                 + " (Description: " + getDescription() + ")"
328                 + " (UID: " + getUid() + ")");
329         pw.println("    System Code: " + getSystemCode());
330         pw.println("    NFCID2: " + getNfcid2());
331         pw.println("    T3tPmm: " + getT3tPmm());
332     }
333 
334     /**
335      * Dump debugging info as NfcFServiceInfoProto
336      *
337      * If the output belongs to a sub message, the caller is responsible for wrapping this function
338      * between {@link ProtoOutputStream#start(long)} and {@link ProtoOutputStream#end(long)}.
339      *
340      * @param proto the ProtoOutputStream to write to
341      */
dumpDebug(ProtoOutputStream proto)342     public void dumpDebug(ProtoOutputStream proto) {
343         Utils.dumpDebugComponentName(getComponent(), proto, NfcFServiceInfoProto.COMPONENT_NAME);
344         proto.write(NfcFServiceInfoProto.DESCRIPTION, getDescription());
345         proto.write(NfcFServiceInfoProto.SYSTEM_CODE, getSystemCode());
346         proto.write(NfcFServiceInfoProto.NFCID2, getNfcid2());
347         proto.write(NfcFServiceInfoProto.T3T_PMM, getT3tPmm());
348     }
349 }
350 
351