| /* |
| * Copyright (C) 2015-2019 NXP Semiconductors |
| * |
| * 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.nfc.cardemulation.ApduServiceInfo; |
| import android.nfc.cardemulation.AidGroup; |
| import android.nfc.cardemulation.NfcAidGroup; |
| import android.nfc.cardemulation.CardEmulation; |
| import android.nfc.cardemulation.HostApduService; |
| import android.nfc.cardemulation.OffHostApduService; |
| import android.os.Parcel; |
| import android.os.Parcelable; |
| import android.graphics.drawable.Drawable; |
| import android.graphics.drawable.BitmapDrawable; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import android.util.Log; |
| import android.util.Xml; |
| import android.content.pm.ResolveInfo; |
| import android.content.pm.ServiceInfo; |
| import android.content.pm.PackageManager.NameNotFoundException; |
| import android.content.res.XmlResourceParser; |
| import com.nxp.nfc.NfcConstants; |
| import java.io.IOException; |
| import android.content.pm.PackageManager; |
| import org.xmlpull.v1.XmlPullParser; |
| import org.xmlpull.v1.XmlPullParserException; |
| import org.xmlpull.v1.XmlSerializer; |
| import android.content.res.Resources; |
| import android.content.res.Resources.NotFoundException; |
| import android.util.AttributeSet; |
| import java.util.Map; |
| import android.graphics.Bitmap; |
| import java.io.FileDescriptor; |
| import java.util.List; |
| import java.io.PrintWriter; |
| import android.content.res.TypedArray; |
| import android.graphics.BitmapFactory; |
| /** |
| * @hide |
| */ |
| public final class NfcApduServiceInfo |
| extends ApduServiceInfo implements Parcelable { |
| static final String TAG = "NfcApduServiceInfo"; |
| |
| // name of secure element |
| static final String SECURE_ELEMENT_ESE = "eSE"; |
| static final String SECURE_ELEMENT_UICC = "UICC"; |
| static final String SECURE_ELEMENT_UICC2 = "UICC2"; |
| // index of secure element |
| public static final int SECURE_ELEMENT_ROUTE_ESE = 1; |
| public static final int SECURE_ELEMENT_ROUTE_UICC = 2; |
| public static final int SECURE_ELEMENT_ROUTE_UICC2 = 0x4; |
| |
| // power state value |
| static final int POWER_STATE_SWITCH_ON = 1; |
| static final int POWER_STATE_SWITCH_OFF = 2; |
| static final int POWER_STATE_BATTERY_OFF = 4; |
| |
| /** |
| * The name of the meta-data element that contains |
| * nxp extended SE information about off host service. |
| */ |
| static final String NXP_NFC_EXT_META_DATA = "com.nxp.nfc.extensions"; |
| |
| /** |
| * Mapping from category to static AID group |
| */ |
| final HashMap<String, NfcAidGroup> mStaticNfcAidGroups; |
| |
| /** |
| * Mapping from category to dynamic AID group |
| */ |
| final HashMap<String, NfcAidGroup> mDynamicNfcAidGroups; |
| |
| /** |
| * The Drawable of the service banner specified by the Application Dynamically |
| * to be stored as byteArray. |
| */ |
| byte[] mByteArrayBanner = null; |
| |
| /** |
| * This says whether the Application can modify the AIDs or not. |
| */ |
| final boolean mModifiable; |
| |
| /** |
| * This says whether the Service is enabled or disabled by the user |
| * By default it is disabled.This is only applicable for OTHER category. |
| * states are as follows |
| * ENABLING(service creation)->ENABLED(Committed to Routing)-> |
| * DISABLING(user requested to disable)->DISABLED(Removed from Routing). |
| * In ENABLED or DISABLING state, this service will be accounted for routing. |
| */ |
| int mServiceState; |
| |
| /** |
| * nxp se extension |
| */ |
| final ESeInfo mSeExtension; |
| /** |
| * @hide |
| */ |
| public NfcApduServiceInfo(ResolveInfo info, boolean onHost, |
| String description, |
| ArrayList<NfcAidGroup> staticNfcAidGroups, |
| ArrayList<NfcAidGroup> dynamicNfcAidGroups, |
| boolean requiresUnlock, int bannerResource, int uid, |
| String settingsActivityName, ESeInfo seExtension, |
| boolean modifiable) { |
| super(info, description, nfcAidGroups2AidGroups(staticNfcAidGroups), |
| nfcAidGroups2AidGroups(dynamicNfcAidGroups), requiresUnlock, |
| bannerResource, uid, settingsActivityName, null, null); |
| this.mModifiable = modifiable; |
| this.mServiceState = NfcConstants.SERVICE_STATE_ENABLING; |
| this.mStaticNfcAidGroups = new HashMap<String, NfcAidGroup>(); |
| this.mDynamicNfcAidGroups = new HashMap<String, NfcAidGroup>(); |
| if (staticNfcAidGroups != null) { |
| for (NfcAidGroup nfcAidGroup : staticNfcAidGroups) { |
| this.mStaticNfcAidGroups.put(nfcAidGroup.getCategory(), nfcAidGroup); |
| } |
| } |
| |
| if (dynamicNfcAidGroups != null) { |
| for (NfcAidGroup nfcAidGroup : dynamicNfcAidGroups) { |
| this.mDynamicNfcAidGroups.put(nfcAidGroup.getCategory(), nfcAidGroup); |
| } |
| } |
| this.mSeExtension = seExtension; |
| } |
| |
| public NfcApduServiceInfo(PackageManager pm, ResolveInfo info, boolean onHost) |
| throws XmlPullParserException, IOException { |
| super(pm, info, onHost); |
| this.mModifiable = false; |
| this.mServiceState = NfcConstants.SERVICE_STATE_ENABLING; |
| ServiceInfo si = info.serviceInfo; |
| XmlResourceParser parser = null; |
| XmlResourceParser extParser = null; |
| try { |
| if (onHost) { |
| parser = si.loadXmlMetaData(pm, HostApduService.SERVICE_META_DATA); |
| if (parser == null) { |
| throw new XmlPullParserException( |
| "No " + HostApduService.SERVICE_META_DATA + " meta-data"); |
| } |
| } else { |
| parser = si.loadXmlMetaData(pm, OffHostApduService.SERVICE_META_DATA); |
| if (parser == null) { |
| throw new XmlPullParserException( |
| "No " + OffHostApduService.SERVICE_META_DATA + " meta-data"); |
| } |
| |
| /* load se extension xml */ |
| extParser = si.loadXmlMetaData(pm, NXP_NFC_EXT_META_DATA); |
| if (extParser == null) { |
| Log.d(TAG, "No " + NXP_NFC_EXT_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 (onHost && !"host-apdu-service".equals(tagName)) { |
| throw new XmlPullParserException( |
| "Meta-data does not start with <host-apdu-service> tag"); |
| } else if (!onHost && !"offhost-apdu-service".equals(tagName)) { |
| throw new XmlPullParserException( |
| "Meta-data does not start with <offhost-apdu-service> tag"); |
| } |
| |
| Resources res = pm.getResourcesForApplication(si.applicationInfo); |
| AttributeSet attrs = Xml.asAttributeSet(parser); |
| |
| mStaticNfcAidGroups = new HashMap<String, NfcAidGroup>(); |
| mDynamicNfcAidGroups = new HashMap<String, NfcAidGroup>(); |
| for (Map.Entry<String, AidGroup> stringaidgroup : |
| mStaticAidGroups.entrySet()) { |
| String category = stringaidgroup.getKey(); |
| AidGroup aidg = stringaidgroup.getValue(); |
| mStaticNfcAidGroups.put(category, new NfcAidGroup(aidg)); |
| } |
| |
| for (Map.Entry<String, AidGroup> stringaidgroup : |
| mDynamicAidGroups.entrySet()) { |
| String category = stringaidgroup.getKey(); |
| AidGroup aidg = stringaidgroup.getValue(); |
| mDynamicNfcAidGroups.put(category, new NfcAidGroup(aidg)); |
| } |
| } catch (NameNotFoundException e) { |
| throw new XmlPullParserException("Unable to create context for: " + |
| si.packageName); |
| } finally { |
| if (parser != null) |
| parser.close(); |
| } |
| if (extParser != null) { |
| try { |
| int eventType = extParser.getEventType(); |
| final int depth = extParser.getDepth(); |
| String seName = null; |
| int powerState = 0; |
| String optParam = null; |
| |
| while (eventType != XmlPullParser.START_TAG && |
| eventType != XmlPullParser.END_DOCUMENT) { |
| eventType = extParser.next(); |
| } |
| String tagName = extParser.getName(); |
| if (!"extensions".equals(tagName)) { |
| throw new XmlPullParserException( |
| "Meta-data does not start with <extensions> tag " + tagName); |
| } |
| while (((eventType = extParser.next()) != XmlPullParser.END_TAG || |
| extParser.getDepth() > depth) && |
| eventType != XmlPullParser.END_DOCUMENT) { |
| tagName = extParser.getName(); |
| |
| if (eventType == XmlPullParser.START_TAG && "se-id".equals(tagName)) { |
| // Get name of eSE |
| seName = extParser.getAttributeValue(null, "name"); |
| if (seName == null || |
| (!seName.equalsIgnoreCase(SECURE_ELEMENT_ESE) && |
| !seName.equalsIgnoreCase(SECURE_ELEMENT_UICC) && |
| !seName.equalsIgnoreCase(SECURE_ELEMENT_UICC2))) { |
| throw new XmlPullParserException("Unsupported se name: " + |
| seName); |
| } |
| } else if (eventType == XmlPullParser.START_TAG && |
| "se-power-state".equals(tagName)) { |
| // Get power state |
| String powerName = extParser.getAttributeValue(null, "name"); |
| boolean powerValue = |
| (extParser.getAttributeValue(null, "value").equals("true")) |
| ? true |
| : false; |
| if (powerName.equalsIgnoreCase("SwitchOn") && powerValue) { |
| powerState |= POWER_STATE_SWITCH_ON; |
| } else if (powerName.equalsIgnoreCase("SwitchOff") && powerValue) { |
| powerState |= POWER_STATE_SWITCH_OFF; |
| } else if (powerName.equalsIgnoreCase("BatteryOff") && powerValue) { |
| powerState |= POWER_STATE_BATTERY_OFF; |
| } |
| } |
| } |
| if (seName != null) { |
| mSeExtension = new ESeInfo(seName.equals(SECURE_ELEMENT_ESE) |
| ? SECURE_ELEMENT_ROUTE_ESE |
| : (seName.equals(SECURE_ELEMENT_UICC) |
| ? SECURE_ELEMENT_ROUTE_UICC |
| : SECURE_ELEMENT_ROUTE_UICC2), |
| powerState); |
| Log.d(TAG, mSeExtension.toString()); |
| } else { |
| mSeExtension = new ESeInfo(-1, 0); |
| Log.d(TAG, mSeExtension.toString()); |
| } |
| } finally { |
| extParser.close(); |
| } |
| } else { |
| if (!onHost) { |
| Log.e(TAG, "SE extension not present, Setting default offhost seID"); |
| mSeExtension = new ESeInfo(SECURE_ELEMENT_ROUTE_UICC, 0); |
| } else { |
| mSeExtension = new ESeInfo(-1, 0); |
| } |
| } |
| } |
| |
| static ArrayList<AidGroup> |
| nfcAidGroups2AidGroups(ArrayList<NfcAidGroup> nfcAidGroup) { |
| ArrayList<AidGroup> aidGroups = new ArrayList<AidGroup>(); |
| if (nfcAidGroup != null) { |
| for (NfcAidGroup nfcag : nfcAidGroup) { |
| AidGroup ag = nfcag.createAidGroup(); |
| aidGroups.add(ag); |
| } |
| } |
| return aidGroups; |
| } |
| |
| public void writeToXml(XmlSerializer out) throws IOException { |
| out.attribute(null, "description", mDescription); |
| String modifiable = ""; |
| if (mModifiable) { |
| modifiable = "true"; |
| } else { |
| modifiable = "false"; |
| } |
| out.attribute(null, "modifiable", modifiable); |
| out.attribute(null, "uid", Integer.toString(mUid)); |
| out.attribute(null, "seId", Integer.toString(mSeExtension.seId)); |
| out.attribute(null, "bannerId", Integer.toString(mBannerResourceId)); |
| for (AidGroup group : mDynamicAidGroups.values()) { |
| group.writeAsXml(out); |
| } |
| } |
| |
| public ResolveInfo getResolveInfo() { return mService; } |
| /** |
| * Returns a consolidated list of AIDs from the AID groups |
| * registered by this service. Note that if a service has both |
| * a static (manifest-based) AID group for a category and a dynamic |
| * AID group, only the dynamically registered AIDs will be returned |
| * for that category. |
| * @return List of AIDs registered by the service |
| */ |
| public ArrayList<String> getAids() { |
| final ArrayList<String> aids = new ArrayList<String>(); |
| for (NfcAidGroup group : getNfcAidGroups()) { |
| aids.addAll(group.getAids()); |
| } |
| return aids; |
| } |
| /** |
| * Returns a consolidated list of AID groups |
| * registered by this service. Note that if a service has both |
| * a static (manifest-based) AID group for a category and a dynamic |
| * AID group, only the dynamically registered AID group will be returned |
| * for that category. |
| * @return List of AIDs registered by the service |
| */ |
| public ArrayList<NfcAidGroup> getNfcAidGroups() { |
| final ArrayList<NfcAidGroup> groups = new ArrayList<NfcAidGroup>(); |
| for (Map.Entry<String, NfcAidGroup> entry : |
| mDynamicNfcAidGroups.entrySet()) { |
| groups.add(entry.getValue()); |
| } |
| for (Map.Entry<String, NfcAidGroup> entry : |
| mStaticNfcAidGroups.entrySet()) { |
| if (!mDynamicNfcAidGroups.containsKey(entry.getKey())) { |
| // Consolidate AID groups - don't return static ones |
| // if a dynamic group exists for the category. |
| groups.add(entry.getValue()); |
| } |
| } |
| return groups; |
| } |
| |
| /** |
| * This is a convenience function to create an ApduServiceInfo object of the |
| * current NfcApduServiceInfo. It is required for legacy functions which |
| * expect an ApduServiceInfo on a Binder interface. |
| * |
| * @return An ApduServiceInfo object which can be correctly serialized as |
| * parcel |
| */ |
| public ApduServiceInfo createApduServiceInfo() { |
| return new ApduServiceInfo( |
| this.getResolveInfo(), this.getDescription(), |
| nfcAidGroups2AidGroups(this.getStaticNfcAidGroups()), |
| nfcAidGroups2AidGroups(this.getDynamicNfcAidGroups()), |
| this.requiresUnlock(), this.getBannerId(), this.getUid(), |
| this.getSettingsActivityName(), null, null); |
| } |
| |
| /** |
| * This api can be used to find the total aid size registered |
| * by this service. |
| * <p> This returns the size of only {@link #CardEmulation.CATEGORY_OTHER}. |
| * <p> This includes both static and dynamic aid groups |
| * @param category The category of the corresponding service.{@link |
| * #CardEmulation.CATEGORY_OTHER}. |
| * @return The aid cache size for particular category. |
| */ |
| public int getAidCacheSize(String category) { |
| int aidSize = 0x00; |
| if (!CardEmulation.CATEGORY_OTHER.equals(category) || |
| !hasCategory(CardEmulation.CATEGORY_OTHER)) { |
| return 0x00; |
| } |
| aidSize = getAidCacheSizeForCategory(CardEmulation.CATEGORY_OTHER); |
| return aidSize; |
| } |
| |
| public int getAidCacheSizeForCategory(String category) { |
| ArrayList<NfcAidGroup> nfcAidGroups = new ArrayList<NfcAidGroup>(); |
| List<String> aids; |
| int aidCacheSize = 0x00; |
| int aidLen = 0x00; |
| nfcAidGroups.addAll(getStaticNfcAidGroups()); |
| nfcAidGroups.addAll(getDynamicNfcAidGroups()); |
| if (nfcAidGroups == null || nfcAidGroups.size() == 0x00) { |
| return aidCacheSize; |
| } |
| for (NfcAidGroup aidCache : nfcAidGroups) { |
| if (!aidCache.getCategory().equals(category)) { |
| continue; |
| } |
| aids = aidCache.getAids(); |
| if (aids == null || aids.size() == 0) { |
| continue; |
| } |
| for (String aid : aids) { |
| aidLen = aid.length(); |
| if (aid.endsWith("*")) { |
| aidLen = aidLen - 1; |
| } |
| aidCacheSize += aidLen >> 1; |
| } |
| } |
| return aidCacheSize; |
| } |
| |
| /** |
| * This api can be used to find the total aids count registered |
| * by this service. |
| * <p> This returns the size of only {@link #CardEmulation.CATEGORY_OTHER}. |
| * <p> This includes both static and dynamic aid groups |
| * @param category The category of the corresponding service.{@link |
| * #CardEmulation.CATEGORY_OTHER}. |
| * @return The num of aids corresponding to particular cateogry |
| */ |
| public int geTotalAidNum(String category) { |
| int aidTotalNum = 0x00; |
| if (!CardEmulation.CATEGORY_OTHER.equals(category) || |
| !hasCategory(CardEmulation.CATEGORY_OTHER)) { |
| return 0x00; |
| } |
| aidTotalNum = getTotalAidNumCategory(CardEmulation.CATEGORY_OTHER); |
| return aidTotalNum; |
| } |
| |
| private int getTotalAidNumCategory(String category) { |
| ArrayList<NfcAidGroup> aidGroups = new ArrayList<NfcAidGroup>(); |
| List<String> aids; |
| int aidTotalNum = 0x00; |
| aidGroups.addAll(getStaticNfcAidGroups()); |
| aidGroups.addAll(getDynamicNfcAidGroups()); |
| if (aidGroups == null || aidGroups.size() == 0x00) { |
| return aidTotalNum; |
| } |
| for (NfcAidGroup aidCache : aidGroups) { |
| if (!aidCache.getCategory().equals(category)) { |
| continue; |
| } |
| aids = aidCache.getAids(); |
| if (aids == null || aids.size() == 0) { |
| continue; |
| } |
| for (String aid : aids) { |
| if (aid != null && aid.length() > 0x00) { |
| aidTotalNum++; |
| } |
| } |
| } |
| return aidTotalNum; |
| } |
| |
| /**@hide */ |
| public ArrayList<NfcAidGroup> getStaticNfcAidGroups() { |
| final ArrayList<NfcAidGroup> groups = new ArrayList<NfcAidGroup>(); |
| |
| for (Map.Entry<String, NfcAidGroup> entry : |
| mStaticNfcAidGroups.entrySet()) { |
| groups.add(entry.getValue()); |
| } |
| return groups; |
| } |
| |
| /**@hide */ |
| public ArrayList<NfcAidGroup> getDynamicNfcAidGroups() { |
| final ArrayList<NfcAidGroup> groups = new ArrayList<NfcAidGroup>(); |
| for (Map.Entry<String, NfcAidGroup> entry : |
| mDynamicNfcAidGroups.entrySet()) { |
| groups.add(entry.getValue()); |
| } |
| return groups; |
| } |
| |
| public String getOtherAidGroupDescription() { |
| String otherAidGroupDescription = null; |
| if (mStaticNfcAidGroups.containsKey(CardEmulation.CATEGORY_OTHER)) |
| otherAidGroupDescription = |
| mStaticNfcAidGroups.get(CardEmulation.CATEGORY_OTHER).description; |
| else if (mDynamicNfcAidGroups.containsKey(CardEmulation.CATEGORY_OTHER)) |
| otherAidGroupDescription = |
| mDynamicNfcAidGroups.get(CardEmulation.CATEGORY_OTHER).description; |
| else |
| Log.e(TAG, "getOtherAidGroupDescription: " |
| + "Aid Group with OTHER category not available"); |
| |
| Log.e(TAG, "getOtherAidGroupDescription: " + otherAidGroupDescription); |
| return otherAidGroupDescription; |
| } |
| |
| public ESeInfo getSEInfo() { return mSeExtension; } |
| |
| public boolean getModifiable() { return mModifiable; } |
| |
| public Bitmap getBitmapBanner() { |
| if (mByteArrayBanner == null) { |
| return null; |
| } |
| Bitmap bitmap = BitmapFactory.decodeByteArray(mByteArrayBanner, 0, |
| mByteArrayBanner.length); |
| return bitmap; |
| } |
| |
| public void setOrReplaceDynamicNfcAidGroup(NfcAidGroup nfcAidGroup) { |
| super.setOrReplaceDynamicAidGroup(nfcAidGroup); |
| mDynamicNfcAidGroups.put(nfcAidGroup.getCategory(), nfcAidGroup); |
| } |
| |
| public NfcAidGroup getDynamicNfcAidGroupForCategory(String category) { |
| return mDynamicNfcAidGroups.get(category); |
| } |
| |
| public boolean removeDynamicNfcAidGroupForCategory(String category) { |
| super.removeDynamicAidGroupForCategory(category); |
| return (mDynamicNfcAidGroups.remove(category) != null); |
| } |
| |
| public Drawable loadBanner(PackageManager pm) { |
| Resources res; |
| Drawable banner; |
| try { |
| res = pm.getResourcesForApplication(mService.serviceInfo.packageName); |
| if (mBannerResourceId == -1) { |
| banner = new BitmapDrawable((Bitmap)getBitmapBanner()); |
| } else { |
| banner = res.getDrawable(mBannerResourceId, null); |
| } |
| return banner; |
| } catch (NotFoundException e) { |
| Log.e(TAG, "Could not load banner."); |
| return null; |
| } catch (NameNotFoundException e) { |
| Log.e(TAG, "Could not load banner."); |
| return null; |
| } |
| } |
| |
| public int getBannerId() { return mBannerResourceId; } |
| |
| @Override |
| public String toString() { |
| StringBuilder out = new StringBuilder("ApduService: "); |
| out.append(getComponent()); |
| out.append(", description: " + mDescription); |
| out.append(", Static AID Groups: "); |
| for (NfcAidGroup nfcAidGroup : mStaticNfcAidGroups.values()) { |
| out.append(nfcAidGroup.toString()); |
| } |
| out.append(", Dynamic AID Groups: "); |
| for (NfcAidGroup nfcAidGroup : mDynamicNfcAidGroups.values()) { |
| out.append(nfcAidGroup.toString()); |
| } |
| return out.toString(); |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (this == o) |
| return true; |
| if (!(o instanceof NfcApduServiceInfo)) |
| return false; |
| NfcApduServiceInfo thatService = (NfcApduServiceInfo)o; |
| |
| return thatService.getComponent().equals(this.getComponent()); |
| } |
| |
| @Override |
| public void writeToParcel(Parcel dest, int flags) { |
| mService.writeToParcel(dest, flags); |
| dest.writeString(mDescription); |
| dest.writeInt(mOnHost ? 1 : 0); |
| dest.writeInt(mStaticNfcAidGroups.size()); |
| if (mStaticNfcAidGroups.size() > 0) { |
| dest.writeTypedList( |
| new ArrayList<NfcAidGroup>(mStaticNfcAidGroups.values())); |
| } |
| dest.writeInt(mDynamicNfcAidGroups.size()); |
| if (mDynamicNfcAidGroups.size() > 0) { |
| dest.writeTypedList( |
| new ArrayList<NfcAidGroup>(mDynamicNfcAidGroups.values())); |
| } |
| dest.writeInt(mRequiresDeviceUnlock ? 1 : 0); |
| dest.writeInt(mBannerResourceId); |
| dest.writeInt(mUid); |
| dest.writeString(mSettingsActivityName); |
| mSeExtension.writeToParcel(dest, flags); |
| dest.writeByteArray(mByteArrayBanner); |
| dest.writeInt(mModifiable ? 1 : 0); |
| dest.writeInt(mServiceState); |
| }; |
| |
| public static final Parcelable.Creator<NfcApduServiceInfo> CREATOR = |
| new Parcelable.Creator<NfcApduServiceInfo>() { |
| @Override |
| public NfcApduServiceInfo createFromParcel(Parcel source) { |
| ResolveInfo info = ResolveInfo.CREATOR.createFromParcel(source); |
| String description = source.readString(); |
| boolean onHost = source.readInt() != 0; |
| ArrayList<NfcAidGroup> staticNfcAidGroups = |
| new ArrayList<NfcAidGroup>(); |
| int numStaticGroups = source.readInt(); |
| if (numStaticGroups > 0) { |
| source.readTypedList(staticNfcAidGroups, NfcAidGroup.CREATOR); |
| } |
| ArrayList<NfcAidGroup> dynamicNfcAidGroups = |
| new ArrayList<NfcAidGroup>(); |
| int numDynamicGroups = source.readInt(); |
| if (numDynamicGroups > 0) { |
| source.readTypedList(dynamicNfcAidGroups, NfcAidGroup.CREATOR); |
| } |
| boolean requiresUnlock = source.readInt() != 0; |
| int bannerResource = source.readInt(); |
| int uid = source.readInt(); |
| String settingsActivityName = source.readString(); |
| ESeInfo seExtension = ESeInfo.CREATOR.createFromParcel(source); |
| byte[] byteArrayBanner = new byte[] {0}; |
| byteArrayBanner = source.createByteArray(); |
| boolean modifiable = source.readInt() != 0; |
| NfcApduServiceInfo service = new NfcApduServiceInfo( |
| info, onHost, description, staticNfcAidGroups, |
| dynamicNfcAidGroups, requiresUnlock, bannerResource, uid, |
| settingsActivityName, seExtension, modifiable); |
| service.setServiceState(CardEmulation.CATEGORY_OTHER, |
| source.readInt()); |
| return service; |
| } |
| |
| @Override |
| public NfcApduServiceInfo[] newArray(int size) { |
| return new NfcApduServiceInfo[size]; |
| } |
| }; |
| public boolean isServiceEnabled(String category) { |
| if (!category.equals(CardEmulation.CATEGORY_OTHER)) { |
| return true; |
| } |
| |
| if ((mServiceState == NfcConstants.SERVICE_STATE_ENABLED) || |
| (mServiceState == NfcConstants.SERVICE_STATE_DISABLING)) { |
| return true; |
| } else { /*SERVICE_STATE_DISABLED or SERVICE_STATE_ENABLING*/ |
| return false; |
| } |
| } |
| |
| /** |
| * This method is invoked before the service is commited to routing table. |
| * mServiceState is previous state of the service, and, |
| * user is now requesting to enable/disable (using flagEnable) this service |
| * before committing all the services to routing table. |
| * @param flagEnable To Enable/Disable the service. |
| * FALSE Disable service |
| * TRUE Enable service |
| */ |
| |
| public void enableService(String category, boolean flagEnable) { |
| if (category != CardEmulation.CATEGORY_OTHER) { |
| return; |
| } |
| Log.d(TAG, "setServiceState:Description:" + mDescription + |
| ":InternalState:" + mServiceState + |
| ":flagEnable:" + flagEnable); |
| if (((mServiceState == NfcConstants.SERVICE_STATE_ENABLED) && |
| (flagEnable == true)) || |
| ((mServiceState == NfcConstants.SERVICE_STATE_DISABLED) && |
| (flagEnable == false)) || |
| ((mServiceState == NfcConstants.SERVICE_STATE_DISABLING) && |
| (flagEnable == false)) || |
| ((mServiceState == NfcConstants.SERVICE_STATE_ENABLING) && |
| (flagEnable == true))) { |
| /*No change in state*/ |
| return; |
| } else if ((mServiceState == NfcConstants.SERVICE_STATE_ENABLED) && |
| (flagEnable == false)) { |
| mServiceState = NfcConstants.SERVICE_STATE_DISABLING; |
| } else if ((mServiceState == NfcConstants.SERVICE_STATE_DISABLED) && |
| (flagEnable == true)) { |
| mServiceState = NfcConstants.SERVICE_STATE_ENABLING; |
| } else if ((mServiceState == NfcConstants.SERVICE_STATE_DISABLING) && |
| (flagEnable == true)) { |
| mServiceState = NfcConstants.SERVICE_STATE_ENABLED; |
| } else if ((mServiceState == NfcConstants.SERVICE_STATE_ENABLING) && |
| (flagEnable == false)) { |
| mServiceState = NfcConstants.SERVICE_STATE_DISABLED; |
| } |
| } |
| |
| public int getServiceState(String category) { |
| if (!category.equals(CardEmulation.CATEGORY_OTHER)) { |
| return NfcConstants.SERVICE_STATE_ENABLED; |
| } |
| |
| return mServiceState; |
| } |
| |
| public int setServiceState(String category, int state) { |
| if (!category.equals(CardEmulation.CATEGORY_OTHER)) { |
| return NfcConstants.SERVICE_STATE_ENABLED; |
| } |
| |
| mServiceState = state; |
| return mServiceState; |
| } |
| |
| /** |
| * Updates the state of the service based on the commit status |
| * This method needs to be invoked after current service is pushed for the |
| * commit to routing table |
| * @param commitStatus Result of the commit. |
| * FALSE if the commit failed. Reason for ex: there was an overflow of |
| * routing table TRUE if the commit was successful |
| */ |
| public void updateServiceCommitStatus(String category, boolean commitStatus) { |
| if (!category.equals(CardEmulation.CATEGORY_OTHER)) { |
| return; |
| } |
| Log.d(TAG, "updateServiceCommitStatus:Description:" + mDescription + |
| ":InternalState:" + mServiceState + |
| ":commitStatus:" + commitStatus); |
| if (commitStatus) { |
| /*Commit was successful and all newly added services were registered, |
| * disabled applications were removed/unregistered from routing entries*/ |
| if (mServiceState == NfcConstants.SERVICE_STATE_DISABLING) { |
| mServiceState = NfcConstants.SERVICE_STATE_DISABLED; |
| } else if (mServiceState == NfcConstants.SERVICE_STATE_ENABLING) { |
| mServiceState = NfcConstants.SERVICE_STATE_ENABLED; |
| } |
| |
| } else { |
| /*Commit failed and all newly added services were not registered |
| * successfully. disabled applications were not successfully disabled*/ |
| if (mServiceState == NfcConstants.SERVICE_STATE_DISABLING) { |
| mServiceState = NfcConstants.SERVICE_STATE_ENABLED; |
| } else if (mServiceState == NfcConstants.SERVICE_STATE_ENABLING) { |
| mServiceState = NfcConstants.SERVICE_STATE_DISABLED; |
| } |
| } |
| } |
| |
| static String serviceStateToString(int state) { |
| switch (state) { |
| case NfcConstants.SERVICE_STATE_DISABLED: |
| return "DISABLED"; |
| case NfcConstants.SERVICE_STATE_ENABLED: |
| return "ENABLED"; |
| case NfcConstants.SERVICE_STATE_ENABLING: |
| return "ENABLING"; |
| case NfcConstants.SERVICE_STATE_DISABLING: |
| return "DISABLING"; |
| default: |
| return "UNKNOWN"; |
| } |
| } |
| public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { |
| super.dump(fd, pw, args); |
| pw.println(" Routing Destination: " + |
| (mOnHost ? "host" : "secure element")); |
| if (hasCategory(CardEmulation.CATEGORY_OTHER)) { |
| pw.println(" Service State: " + serviceStateToString(mServiceState)); |
| } |
| } |
| |
| public static class ESeInfo implements Parcelable { |
| final int seId; |
| final int powerState; |
| |
| public ESeInfo(int seId, int powerState) { |
| this.seId = seId; |
| this.powerState = powerState; |
| } |
| |
| public int getSeId() { return seId; } |
| |
| public int getPowerState() { return powerState; } |
| |
| @Override |
| public String toString() { |
| StringBuilder out = new StringBuilder( |
| "seId: " + seId + ",Power state: [switchOn: " + |
| ((powerState & POWER_STATE_SWITCH_ON) != 0) + |
| ",switchOff: " + ((powerState & POWER_STATE_SWITCH_OFF) != 0) + |
| ",batteryOff: " + ((powerState & POWER_STATE_BATTERY_OFF) != 0) + |
| "]"); |
| return out.toString(); |
| } |
| |
| @Override |
| public int describeContents() { |
| return 0; |
| } |
| |
| @Override |
| public void writeToParcel(Parcel dest, int flags) { |
| dest.writeInt(seId); |
| dest.writeInt(powerState); |
| } |
| |
| public static final Parcelable.Creator<NfcApduServiceInfo.ESeInfo> CREATOR = |
| new Parcelable.Creator<NfcApduServiceInfo.ESeInfo>() { |
| @Override |
| public ESeInfo createFromParcel(Parcel source) { |
| int seId = source.readInt(); |
| int powerState = source.readInt(); |
| return new ESeInfo(seId, powerState); |
| } |
| |
| @Override |
| public ESeInfo[] newArray(int size) { |
| return new ESeInfo[size]; |
| } |
| }; |
| } |
| } |