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

Commit 5dd29443 authored by Brad Lassey's avatar Brad Lassey
Browse files

Introduce dedicated PollingFrame class to replace the use of Bundles per

API Council feedback

Bug: b/315131060
Test: Tested with CTS
Change-Id: I9f69dba67d3f4d204da05148f20cbfefc2a8c89a
parent dd8353a6
Loading
Loading
Loading
Loading
+12 −5
Original line number Diff line number Diff line
@@ -228,14 +228,10 @@ package android.nfc.cardemulation {
    method public final android.os.IBinder onBind(android.content.Intent);
    method public abstract void onDeactivated(int);
    method public abstract byte[] processCommandApdu(byte[], android.os.Bundle);
    method @FlaggedApi("android.nfc.nfc_read_polling_loop") public void processPollingFrames(@NonNull java.util.List<android.os.Bundle>);
    method @FlaggedApi("android.nfc.nfc_read_polling_loop") public void processPollingFrames(@NonNull java.util.List<android.nfc.cardemulation.PollingFrame>);
    method public final void sendResponseApdu(byte[]);
    field public static final int DEACTIVATION_DESELECTED = 1; // 0x1
    field public static final int DEACTIVATION_LINK_LOSS = 0; // 0x0
    field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final String KEY_POLLING_LOOP_DATA = "android.nfc.cardemulation.DATA";
    field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final String KEY_POLLING_LOOP_GAIN = "android.nfc.cardemulation.GAIN";
    field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final String KEY_POLLING_LOOP_TIMESTAMP = "android.nfc.cardemulation.TIMESTAMP";
    field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final String KEY_POLLING_LOOP_TYPE = "android.nfc.cardemulation.TYPE";
    field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final char POLLING_LOOP_TYPE_A = 65; // 0x0041 'A'
    field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final char POLLING_LOOP_TYPE_B = 66; // 0x0042 'B'
    field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final char POLLING_LOOP_TYPE_F = 70; // 0x0046 'F'
@@ -274,6 +270,17 @@ package android.nfc.cardemulation {
    field public static final String SERVICE_META_DATA = "android.nfc.cardemulation.off_host_apdu_service";
  }

  @FlaggedApi("android.nfc.nfc_read_polling_loop") public final class PollingFrame implements android.os.Parcelable {
    ctor public PollingFrame(char, @Nullable byte[], int, int);
    method public int describeContents();
    method @NonNull public byte[] getData();
    method public int getGain();
    method public int getTimestamp();
    method public char getType();
    method public void writeToParcel(@NonNull android.os.Parcel, int);
    field @NonNull public static final android.os.Parcelable.Creator<android.nfc.cardemulation.PollingFrame> CREATOR;
  }

}

package android.nfc.tech {
+8 −1
Original line number Diff line number Diff line
@@ -38,6 +38,8 @@ import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.nfc.cardemulation.HostApduService;
import android.nfc.cardemulation.PollingFrame;
import android.nfc.tech.MifareClassic;
import android.nfc.tech.Ndef;
import android.nfc.tech.NfcA;
@@ -2799,7 +2801,12 @@ public final class NfcAdapter {
     */
    @TestApi
    @FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP)
    public void notifyPollingLoop(@NonNull Bundle frame) {
    public void notifyPollingLoop(@NonNull PollingFrame pollingFrame) {
        Bundle frame = new Bundle();
        frame.putChar(HostApduService.KEY_POLLING_LOOP_TYPE, pollingFrame.getType());
        frame.putByte(HostApduService.KEY_POLLING_LOOP_GAIN, (byte) pollingFrame.getGain());
        frame.putByteArray(HostApduService.KEY_POLLING_LOOP_DATA, pollingFrame.getData());
        frame.putInt(HostApduService.KEY_POLLING_LOOP_TIMESTAMP, pollingFrame.getTimestamp());
        try {
            if (sService == null) {
                attemptDeadServiceRecovery(null);
+19 −9
Original line number Diff line number Diff line
@@ -245,7 +245,9 @@ public abstract class HostApduService extends Service {

    /**
     * KEY_POLLING_LOOP_TYPE is the Bundle key for the type of
     * polling loop frame in the Bundle passed to {@link #processPollingFrames(List)}
     * polling loop frame in the Bundle included in MSG_POLLING_LOOP.
     *
     * @hide
     */
    @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
    public static final String KEY_POLLING_LOOP_TYPE = "android.nfc.cardemulation.TYPE";
@@ -300,24 +302,27 @@ public abstract class HostApduService extends Service {

    /**
     * KEY_POLLING_LOOP_DATA is the Bundle key for the raw data of captured from
     * the polling loop frame in the Bundle passed to {@link #processPollingFrames(List)}
     * when the frame type isn't recognized.
     * the polling loop frame in the Bundle included in MSG_POLLING_LOOP.
     *
     * @hide
     */
    @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
    public static final String KEY_POLLING_LOOP_DATA = "android.nfc.cardemulation.DATA";

    /**
     * KEY_POLLING_LOOP_GAIN is the Bundle key for the field strength of
     * the polling loop frame in the Bundle passed to {@link #processPollingFrames(List)}
     * when the frame type isn't recognized.
     * the polling loop frame in the Bundle included in MSG_POLLING_LOOP.
     *
     * @hide
     */
    @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
    public static final String KEY_POLLING_LOOP_GAIN = "android.nfc.cardemulation.GAIN";

    /**
     * KEY_POLLING_LOOP_TIMESTAMP is the Bundle key for the timestamp of
     * the polling loop frame in the Bundle passed to {@link #processPollingFrames(List)}
     * when the frame type isn't recognized.
     * the polling loop frame in the Bundle included in MSG_POLLING_LOOP.
     *
     * @hide
     */
    @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
    public static final String KEY_POLLING_LOOP_TIMESTAMP = "android.nfc.cardemulation.TIMESTAMP";
@@ -407,7 +412,12 @@ public abstract class HostApduService extends Service {
                    ArrayList<Bundle> frames =
                            msg.getData().getParcelableArrayList(KEY_POLLING_LOOP_FRAMES_BUNDLE,
                            Bundle.class);
                    processPollingFrames(frames);
                    ArrayList<PollingFrame> pollingFrames =
                            new ArrayList<PollingFrame>(frames.size());
                    for (Bundle frame : frames) {
                        pollingFrames.add(new PollingFrame(frame));
                    }
                    processPollingFrames(pollingFrames);
                    break;
            default:
                super.handleMessage(msg);
@@ -482,7 +492,7 @@ public abstract class HostApduService extends Service {
     * @param frame A description of the polling frame.
     */
    @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
    public void processPollingFrames(@NonNull List<Bundle> frame) {
    public void processPollingFrames(@NonNull List<PollingFrame> frame) {
    }

    /**
+143 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;

/**
 * Polling Frames represent data about individual frames of an NFC polling loop. These frames will
 * be deliverd to subclasses of {@link HostApduService} that have registered filters with
 * {@link CardEmulation#registerPollingLoopFilterForService(ComponentName, String)} that match a
 * given frame in a loop and will be delivered through calls to
 * {@link HostApduService#processPollingFrames(List)}.
 */
@FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
public final class PollingFrame implements Parcelable{
    private char mType;
    private byte[] mData;
    private int mGain;
    private int mTimestamp;

    public static final @NonNull Parcelable.Creator<PollingFrame> CREATOR =
            new Parcelable.Creator<PollingFrame>() {

                @Override
                public PollingFrame createFromParcel(Parcel source) {
                    return new PollingFrame(source.readBundle());
                }

                @Override
                public PollingFrame[] newArray(int size) {
                    return new PollingFrame[0];
                }
            };

    PollingFrame(Bundle frame) {
        mType = frame.getChar(HostApduService.KEY_POLLING_LOOP_TYPE);
        mData = frame.getByteArray(HostApduService.KEY_POLLING_LOOP_DATA);
        if (mData == null) {
            mData = new byte[0];
        }
        mGain = frame.getByte(HostApduService.KEY_POLLING_LOOP_GAIN);
        mTimestamp = frame.getInt(HostApduService.KEY_POLLING_LOOP_TIMESTAMP);
    }

    public PollingFrame(char type, @Nullable byte[] data, int gain, int timestamp) {
        mType = type;
        mData = data == null ? new byte[0] : data;
        mGain = gain;
        mTimestamp = timestamp;
    }

    private PollingFrame(Parcel source) {
        mType = (char) source.readInt();
        source.readByteArray(mData);
        mGain = source.readInt();
        mTimestamp = source.readInt();
    }

    /**
     * Returns the type of frame for this polling loop frame.
     *
     * The possible return values are:
     * <ul>
     *   <li>{@link HostApduService#POLLING_LOOP_TYPE_ON}</li>
     *   <li>{@link HostApduService#POLLING_LOOP_TYPE_OFF}</li>
     *   <li>{@link HostApduService#POLLING_LOOP_TYPE_A}</li>
     *   <li>{@link HostApduService#POLLING_LOOP_TYPE_B}</li>
     *   <li>{@link HostApduService#POLLING_LOOP_TYPE_F}</li>
     * </ul>
     */
    public char getType() {
        return mType;
    }

    /**
     * Returns the raw data from the polling type frame.
     */
    public @NonNull byte[] getData() {
        return mData;
    }

    /**
     * Returns the gain representing the field strength of the NFC field when this polling loop
     * frame was observed.
     */
    public int getGain() {
        return mGain;
    }

    /**
     * Returns the timestamp of when the polling loop frame was observed in milliseconds. These
     * timestamps are relative and not absolute and should only be used fro comparing the timing of
     * frames relative to each other.
     * @return the timestamp in milliseconds
     */
    public int getTimestamp() {
        return mTimestamp;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(@NonNull Parcel dest, int flags) {
        dest.writeBundle(toBundle());
    }

    /**
     *
     * @hide
     * @return a Bundle representing this frame
     */
    public Bundle toBundle() {
        Bundle frame = new Bundle();
        frame.putInt(HostApduService.KEY_POLLING_LOOP_TYPE, getType());
        frame.putByte(HostApduService.KEY_POLLING_LOOP_GAIN, (byte) getGain());
        frame.putByteArray(HostApduService.KEY_POLLING_LOOP_DATA, getData());
        frame.putInt(HostApduService.KEY_POLLING_LOOP_TIMESTAMP, getTimestamp());
        return frame;
    }
}