| /* |
| * 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()); |
| } |
| } |
| |