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.ResolveInfo;
     22 import android.content.pm.ServiceInfo;
     23 import android.content.pm.PackageManager.NameNotFoundException;
     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     /**
     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