Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b1917ee5 authored by Brad Lassey's avatar Brad Lassey
Browse files

Add API for polling loop pattern filters based on developer feedback.

Bug: 329147383
Test: Tested with new CTS test
Change-Id: I0bca755d700e0852931fad75affee30f114e4bc6
parent e4fdb75f
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -10403,6 +10403,7 @@ package android.nfc.cardemulation {
  @FlaggedApi("android.nfc.enable_nfc_mainline") public final class ApduServiceInfo implements android.os.Parcelable {
    ctor @FlaggedApi("android.nfc.enable_nfc_mainline") public ApduServiceInfo(@NonNull android.content.pm.PackageManager, @NonNull android.content.pm.ResolveInfo, boolean) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
    method @FlaggedApi("android.nfc.nfc_read_polling_loop") public void addPollingLoopFilter(@NonNull String, boolean);
    method @FlaggedApi("android.nfc.nfc_read_polling_loop") public void addPollingLoopPatternFilter(@NonNull String, boolean);
    method @FlaggedApi("android.nfc.enable_nfc_mainline") public int describeContents();
    method @FlaggedApi("android.nfc.enable_nfc_mainline") public void dump(@NonNull android.os.ParcelFileDescriptor, @NonNull java.io.PrintWriter, @NonNull String[]);
    method @FlaggedApi("android.nfc.enable_nfc_mainline") public void dumpDebug(@NonNull android.util.proto.ProtoOutputStream);
@@ -10414,6 +10415,7 @@ package android.nfc.cardemulation {
    method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public android.nfc.cardemulation.AidGroup getDynamicAidGroupForCategory(@NonNull String);
    method @FlaggedApi("android.nfc.enable_nfc_mainline") @Nullable public String getOffHostSecureElement();
    method @FlaggedApi("android.nfc.nfc_read_polling_loop") @NonNull public java.util.List<java.lang.String> getPollingLoopFilters();
    method @FlaggedApi("android.nfc.nfc_read_polling_loop") @NonNull public java.util.List<java.util.regex.Pattern> getPollingLoopPatternFilters();
    method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public java.util.List<java.lang.String> getPrefixAids();
    method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public String getSettingsActivityName();
    method @FlaggedApi("android.nfc.nfc_read_polling_loop") public boolean getShouldAutoTransact(@NonNull String);
@@ -10428,6 +10430,7 @@ package android.nfc.cardemulation {
    method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public CharSequence loadLabel(@NonNull android.content.pm.PackageManager);
    method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public boolean removeDynamicAidGroupForCategory(@NonNull String);
    method @FlaggedApi("android.nfc.nfc_read_polling_loop") public void removePollingLoopFilter(@NonNull String);
    method @FlaggedApi("android.nfc.nfc_read_polling_loop") public void removePollingLoopPatternFilter(@NonNull String);
    method @FlaggedApi("android.nfc.enable_nfc_mainline") public boolean requiresScreenOn();
    method @FlaggedApi("android.nfc.enable_nfc_mainline") public boolean requiresUnlock();
    method @FlaggedApi("android.nfc.enable_nfc_mainline") public void resetOffHostSecureElement();
+15 −0
Original line number Diff line number Diff line
@@ -4484,6 +4484,21 @@
        <attr name="autoTransact" format="boolean"/>
    </declare-styleable>
    <!-- Specify one or more <code>polling-loop-pattern-filter</code> elements inside a
         <code>host-apdu-service</code> to indicate polling loop frames that
         your service can handle. -->
    <!-- @FlaggedApi("android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP") -->
    <declare-styleable name="PollingLoopPatternFilter">
        <!-- The patter to match polling loop frames to, must to be compatible with
         {@link java.util.regex.Pattern#compile(String)} and only contain hexadecimal numbers and
         `.`, `?` and `*` operators. This attribute is mandatory. -->
        <attr name="name" />
        <!-- Whether or not the system should automatically start a transaction when this polling
         loop filter matches. If not set, default value is false. -->
        <!-- @FlaggedApi("android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP") -->
        <attr name="autoTransact" format="boolean"/>
    </declare-styleable>
    <!-- Use <code>host-nfcf-service</code> as the root tag of the XML resource that
         describes an {@link android.nfc.cardemulation.HostNfcFService} service, which
         is referenced from its {@link android.nfc.cardemulation.HostNfcFService#SERVICE_META_DATA}
+3 −0
Original line number Diff line number Diff line
@@ -205,7 +205,10 @@ package android.nfc.cardemulation {
    method public boolean isDefaultServiceForCategory(android.content.ComponentName, String);
    method public boolean registerAidsForService(android.content.ComponentName, String, java.util.List<java.lang.String>);
    method @FlaggedApi("android.nfc.nfc_read_polling_loop") public boolean registerPollingLoopFilterForService(@NonNull android.content.ComponentName, @NonNull String, boolean);
    method @FlaggedApi("android.nfc.nfc_read_polling_loop") public boolean registerPollingLoopPatternFilterForService(@NonNull android.content.ComponentName, @NonNull String, boolean);
    method public boolean removeAidsForService(android.content.ComponentName, String);
    method @FlaggedApi("android.nfc.nfc_read_polling_loop") public boolean removePollingLoopFilterForService(@NonNull android.content.ComponentName, @NonNull String);
    method @FlaggedApi("android.nfc.nfc_read_polling_loop") public boolean removePollingLoopPatternFilterForService(@NonNull android.content.ComponentName, @NonNull String);
    method @NonNull @RequiresPermission(android.Manifest.permission.NFC) public boolean setOffHostForService(@NonNull android.content.ComponentName, @NonNull String);
    method public boolean setPreferredService(android.app.Activity, android.content.ComponentName);
    method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean setShouldDefaultToObserveModeForService(@NonNull android.content.ComponentName, boolean);
+3 −0
Original line number Diff line number Diff line
@@ -33,10 +33,13 @@ interface INfcCardEmulation
    boolean setShouldDefaultToObserveModeForService(int userId, in android.content.ComponentName service, boolean enable);
    boolean registerAidGroupForService(int userHandle, in ComponentName service, in AidGroup aidGroup);
    boolean registerPollingLoopFilterForService(int userHandle, in ComponentName service, in String pollingLoopFilter, boolean autoTransact);
    boolean registerPollingLoopPatternFilterForService(int userHandle, in ComponentName service, in String pollingLoopPatternFilter, boolean autoTransact);
    boolean setOffHostForService(int userHandle, in ComponentName service, in String offHostSecureElement);
    boolean unsetOffHostForService(int userHandle, in ComponentName service);
    AidGroup getAidGroupForService(int userHandle, in ComponentName service, String category);
    boolean removeAidGroupForService(int userHandle, in ComponentName service, String category);
    boolean removePollingLoopFilterForService(int userHandle, in ComponentName service, in String pollingLoopFilter);
    boolean removePollingLoopPatternFilterForService(int userHandle, in ComponentName service, in String pollingLoopPatternFilter);
    List<ApduServiceInfo> getServices(int userHandle, in String category);
    boolean setPreferredService(in ComponentName service);
    boolean unsetPreferredService();
+76 −5
Original line number Diff line number Diff line
@@ -108,6 +108,8 @@ public final class ApduServiceInfo implements Parcelable {

    private final Map<String, Boolean> mAutoTransact;

    private final Map<Pattern, Boolean> mAutoTransactPatterns;

    /**
     * Whether this service should only be started when the device is unlocked.
     */
@@ -179,7 +181,7 @@ public final class ApduServiceInfo implements Parcelable {
        this(info, onHost, description, staticAidGroups, dynamicAidGroups,
                requiresUnlock, requiresScreenOn, bannerResource, uid,
                settingsActivityName, offHost, staticOffHost, isEnabled,
                new HashMap<String, Boolean>());
                new HashMap<String, Boolean>(), new HashMap<Pattern, Boolean>());
    }

    /**
@@ -189,12 +191,13 @@ public final class ApduServiceInfo implements Parcelable {
            List<AidGroup> staticAidGroups, List<AidGroup> dynamicAidGroups,
            boolean requiresUnlock, boolean requiresScreenOn, int bannerResource, int uid,
            String settingsActivityName, String offHost, String staticOffHost, boolean isEnabled,
            HashMap<String, Boolean> autoTransact) {
            Map<String, Boolean> autoTransact, Map<Pattern, Boolean> autoTransactPatterns) {
        this.mService = info;
        this.mDescription = description;
        this.mStaticAidGroups = new HashMap<String, AidGroup>();
        this.mDynamicAidGroups = new HashMap<String, AidGroup>();
        this.mAutoTransact = autoTransact;
        this.mAutoTransactPatterns = autoTransactPatterns;
        this.mOffHostName = offHost;
        this.mStaticOffHostName = staticOffHost;
        this.mOnHost = onHost;
@@ -314,6 +317,7 @@ public final class ApduServiceInfo implements Parcelable {
            mStaticAidGroups = new HashMap<String, AidGroup>();
            mDynamicAidGroups = new HashMap<String, AidGroup>();
            mAutoTransact = new HashMap<String, Boolean>();
            mAutoTransactPatterns = new HashMap<Pattern, Boolean>();
            mOnHost = onHost;

            final int depth = parser.getDepth();
@@ -408,6 +412,18 @@ public final class ApduServiceInfo implements Parcelable {
                            false);
                    mAutoTransact.put(plf, autoTransact);
                    a.recycle();
                } else if (eventType == XmlPullParser.START_TAG
                        && "polling-loop-pattern-filter".equals(tagName) && currentGroup == null) {
                    final TypedArray a = res.obtainAttributes(attrs,
                            com.android.internal.R.styleable.PollingLoopPatternFilter);
                    String plf = a.getString(
                            com.android.internal.R.styleable.PollingLoopPatternFilter_name)
                                    .toUpperCase(Locale.ROOT);
                    boolean autoTransact = a.getBoolean(
                            com.android.internal.R.styleable.PollingLoopFilter_autoTransact,
                            false);
                    mAutoTransactPatterns.put(Pattern.compile(plf), autoTransact);
                    a.recycle();
                }
            }
        } catch (NameNotFoundException e) {
@@ -481,7 +497,30 @@ public final class ApduServiceInfo implements Parcelable {
     */
    @FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP)
    public boolean getShouldAutoTransact(@NonNull String plf) {
        return mAutoTransact.getOrDefault(plf.toUpperCase(Locale.ROOT), false);
        if (mAutoTransact.getOrDefault(plf.toUpperCase(Locale.ROOT), false)) {
            return true;
        }
        List<Pattern> patternMatches = mAutoTransactPatterns.keySet().stream()
                .filter(p -> p.matcher(plf).matches()).toList();
        if (patternMatches == null || patternMatches.size() == 0) {
            return false;
        }
        for (Pattern patternMatch : patternMatches) {
            if (mAutoTransactPatterns.get(patternMatch)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Returns the current polling loop pattern filters for this service.
     * @return List of polling loop pattern filters.
     */
    @FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP)
    @NonNull
    public List<Pattern> getPollingLoopPatternFilters() {
        return new ArrayList<>(mAutoTransactPatterns.keySet());
    }

    /**
@@ -683,7 +722,7 @@ public final class ApduServiceInfo implements Parcelable {
     * Add a Polling Loop Filter. Custom NFC polling frames that match this filter will be
     * delivered to {@link HostApduService#processPollingFrames(List)}. Adding a key with this
     * multiple times will cause the value to be overwritten each time.
     * @param pollingLoopFilter the polling loop filter to add, must be a  valide hexadecimal string
     * @param pollingLoopFilter the polling loop filter to add, must be a valid hexadecimal string
     */
    @FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP)
    public void addPollingLoopFilter(@NonNull String pollingLoopFilter,
@@ -702,6 +741,31 @@ public final class ApduServiceInfo implements Parcelable {
        mAutoTransact.remove(pollingLoopFilter.toUpperCase(Locale.ROOT));
    }

    /**
     * Add a Polling Loop Pattern Filter. Custom NFC polling frames that match this filter will be
     * delivered to {@link HostApduService#processPollingFrames(List)}. Adding a key with this
     * multiple times will cause the value to be overwritten each time.
     * @param pollingLoopPatternFilter the polling loop pattern filter to add, must be a valid
     *                                regex to match a hexadecimal string
     */
    @FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP)
    public void addPollingLoopPatternFilter(@NonNull String pollingLoopPatternFilter,
            boolean autoTransact) {
        mAutoTransactPatterns.put(Pattern.compile(pollingLoopPatternFilter), autoTransact);

    }

    /**
     * Remove a Polling Loop Pattern Filter. Custom NFC polling frames that match this filter will
     * no longer be delivered to {@link HostApduService#processPollingFrames(List)}.
     * @param pollingLoopPatternFilter this polling loop filter to add.
     */
    @FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP)
    public void removePollingLoopPatternFilter(@NonNull String pollingLoopPatternFilter) {
        mAutoTransactPatterns.remove(
                Pattern.compile(pollingLoopPatternFilter.toUpperCase(Locale.ROOT)));
    }

    /**
     * Sets the off host Secure Element.
     * @param  offHost  Secure Element to set. Only accept strings with prefix SIM or prefix eSE.
@@ -856,6 +920,8 @@ public final class ApduServiceInfo implements Parcelable {
        dest.writeInt(mCategoryOtherServiceEnabled ? 1 : 0);
        dest.writeInt(mAutoTransact.size());
        dest.writeMap(mAutoTransact);
        dest.writeInt(mAutoTransactPatterns.size());
        dest.writeMap(mAutoTransactPatterns);
    };

    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
@@ -889,10 +955,15 @@ public final class ApduServiceInfo implements Parcelable {
                            new HashMap<String, Boolean>(autoTransactSize);
                    source.readMap(autoTransact, getClass().getClassLoader(),
                            String.class, Boolean.class);
                    int autoTransactPatternSize = source.readInt();
                    HashMap<Pattern, Boolean> autoTransactPatterns =
                            new HashMap<Pattern, Boolean>(autoTransactSize);
                    source.readMap(autoTransactPatterns, getClass().getClassLoader(),
                            Pattern.class, Boolean.class);
                    return new ApduServiceInfo(info, onHost, description, staticAidGroups,
                            dynamicAidGroups, requiresUnlock, requiresScreenOn, bannerResource, uid,
                            settingsActivityName, offHostName, staticOffHostName,
                            isEnabled, autoTransact);
                            isEnabled, autoTransact, autoTransactPatterns);
                }

                @Override
Loading