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