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