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