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