/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.nfc.cardemulation; import android.content.ComponentName; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.graphics.drawable.Drawable; import android.os.Parcel; import android.os.Parcelable; import android.util.AttributeSet; import android.util.Log; import android.util.Xml; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.FileDescriptor; import java.io.IOException; import java.io.PrintWriter; /** * @hide */ public final class NfcFServiceInfo implements Parcelable { static final String TAG = "NfcFServiceInfo"; /** * The service that implements this */ final ResolveInfo mService; /** * Description of the service */ final String mDescription; /** * System Code of the service */ final String mSystemCode; /** * System Code of the service registered by API */ String mDynamicSystemCode; /** * NFCID2 of the service */ final String mNfcid2; /** * NFCID2 of the service registered by API */ String mDynamicNfcid2; /** * The uid of the package the service belongs to */ final int mUid; /** * @hide */ public NfcFServiceInfo(ResolveInfo info, String description, String systemCode, String dynamicSystemCode, String nfcid2, String dynamicNfcid2, int uid) { this.mService = info; this.mDescription = description; this.mSystemCode = systemCode; this.mDynamicSystemCode = dynamicSystemCode; this.mNfcid2 = nfcid2; this.mDynamicNfcid2 = dynamicNfcid2; this.mUid = uid; } public NfcFServiceInfo(PackageManager pm, ResolveInfo info) throws XmlPullParserException, IOException { ServiceInfo si = info.serviceInfo; XmlResourceParser parser = null; try { parser = si.loadXmlMetaData(pm, HostNfcFService.SERVICE_META_DATA); if (parser == null) { throw new XmlPullParserException("No " + HostNfcFService.SERVICE_META_DATA + " meta-data"); } int eventType = parser.getEventType(); while (eventType != XmlPullParser.START_TAG && eventType != XmlPullParser.END_DOCUMENT) { eventType = parser.next(); } String tagName = parser.getName(); if (!"host-nfcf-service".equals(tagName)) { throw new XmlPullParserException( "Meta-data does not start with <host-nfcf-service> tag"); } Resources res = pm.getResourcesForApplication(si.applicationInfo); AttributeSet attrs = Xml.asAttributeSet(parser); TypedArray sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.HostNfcFService); mService = info; mDescription = sa.getString( com.android.internal.R.styleable.HostNfcFService_description); mDynamicSystemCode = null; mDynamicNfcid2 = null; sa.recycle(); String systemCode = null; String nfcid2 = null; final int depth = parser.getDepth(); while (((eventType = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth) && eventType != XmlPullParser.END_DOCUMENT) { tagName = parser.getName(); if (eventType == XmlPullParser.START_TAG && "system-code-filter".equals(tagName) && systemCode == null) { final TypedArray a = res.obtainAttributes(attrs, com.android.internal.R.styleable.SystemCodeFilter); systemCode = a.getString( com.android.internal.R.styleable.SystemCodeFilter_name).toUpperCase(); if (!NfcFCardEmulation.isValidSystemCode(systemCode) && !systemCode.equalsIgnoreCase("NULL")) { Log.e(TAG, "Invalid System Code: " + systemCode); systemCode = null; } a.recycle(); } else if (eventType == XmlPullParser.START_TAG && "nfcid2-filter".equals(tagName) && nfcid2 == null) { final TypedArray a = res.obtainAttributes(attrs, com.android.internal.R.styleable.Nfcid2Filter); nfcid2 = a.getString( com.android.internal.R.styleable.Nfcid2Filter_name).toUpperCase(); if (!nfcid2.equalsIgnoreCase("RANDOM") && !nfcid2.equalsIgnoreCase("NULL") && !NfcFCardEmulation.isValidNfcid2(nfcid2)) { Log.e(TAG, "Invalid NFCID2: " + nfcid2); nfcid2 = null; } a.recycle(); } } mSystemCode = (systemCode == null ? "NULL" : systemCode); mNfcid2 = (nfcid2 == null ? "NULL" : nfcid2); } catch (NameNotFoundException e) { throw new XmlPullParserException("Unable to create context for: " + si.packageName); } finally { if (parser != null) parser.close(); } // Set uid mUid = si.applicationInfo.uid; } public ComponentName getComponent() { return new ComponentName(mService.serviceInfo.packageName, mService.serviceInfo.name); } public String getSystemCode() { return (mDynamicSystemCode == null ? mSystemCode : mDynamicSystemCode); } public void setOrReplaceDynamicSystemCode(String systemCode) { mDynamicSystemCode = systemCode; } public String getNfcid2() { return (mDynamicNfcid2 == null ? mNfcid2 : mDynamicNfcid2); } public void setOrReplaceDynamicNfcid2(String nfcid2) { mDynamicNfcid2 = nfcid2; } public String getDescription() { return mDescription; } public int getUid() { return mUid; } public CharSequence loadLabel(PackageManager pm) { return mService.loadLabel(pm); } public Drawable loadIcon(PackageManager pm) { return mService.loadIcon(pm); } @Override public String toString() { StringBuilder out = new StringBuilder("NfcFService: "); out.append(getComponent()); out.append(", description: " + mDescription); out.append(", System Code: " + mSystemCode); if (mDynamicSystemCode != null) { out.append(", dynamic System Code: " + mDynamicSystemCode); } out.append(", NFCID2: " + mNfcid2); if (mDynamicNfcid2 != null) { out.append(", dynamic NFCID2: " + mDynamicNfcid2); } return out.toString(); } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof NfcFServiceInfo)) return false; NfcFServiceInfo thatService = (NfcFServiceInfo) o; if (!thatService.getComponent().equals(this.getComponent())) return false; if (!thatService.mSystemCode.equalsIgnoreCase(this.mSystemCode)) return false; if (!thatService.mNfcid2.equalsIgnoreCase(this.mNfcid2)) return false; return true; } @Override public int hashCode() { return getComponent().hashCode(); } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { mService.writeToParcel(dest, flags); dest.writeString(mDescription); dest.writeString(mSystemCode); dest.writeInt(mDynamicSystemCode != null ? 1 : 0); if (mDynamicSystemCode != null) { dest.writeString(mDynamicSystemCode); } dest.writeString(mNfcid2); dest.writeInt(mDynamicNfcid2 != null ? 1 : 0); if (mDynamicNfcid2 != null) { dest.writeString(mDynamicNfcid2); } dest.writeInt(mUid); }; public static final Parcelable.Creator<NfcFServiceInfo> CREATOR = new Parcelable.Creator<NfcFServiceInfo>() { @Override public NfcFServiceInfo createFromParcel(Parcel source) { ResolveInfo info = ResolveInfo.CREATOR.createFromParcel(source); String description = source.readString(); String systemCode = source.readString(); String dynamicSystemCode = null; if (source.readInt() != 0) { dynamicSystemCode = source.readString(); } String nfcid2 = source.readString(); String dynamicNfcid2 = null; if (source.readInt() != 0) { dynamicNfcid2 = source.readString(); } int uid = source.readInt(); NfcFServiceInfo service = new NfcFServiceInfo(info, description, systemCode, dynamicSystemCode, nfcid2, dynamicNfcid2, uid); return service; } @Override public NfcFServiceInfo[] newArray(int size) { return new NfcFServiceInfo[size]; } }; public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println(" " + getComponent() + " (Description: " + getDescription() + ")"); pw.println(" System Code: " + getSystemCode()); pw.println(" NFCID2: " + getNfcid2()); } }