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