diff options
| author | 2024-02-16 00:02:52 -0500 | |
|---|---|---|
| committer | 2024-02-21 03:36:29 +0000 | |
| commit | 5dd29443cb254e44f41f22b2cc29ba2775ebcd79 (patch) | |
| tree | 38e3f0aafe5075e476f6ebe6711a67528511b9a1 | |
| parent | dd8353a6d390c4985c2748d9b4db136347c85a3b (diff) | |
Introduce dedicated PollingFrame class to replace the use of Bundles per
API Council feedback
Bug: b/315131060
Test: Tested with CTS
Change-Id: I9f69dba67d3f4d204da05148f20cbfefc2a8c89a
| -rw-r--r-- | nfc/api/current.txt | 17 | ||||
| -rw-r--r-- | nfc/java/android/nfc/NfcAdapter.java | 9 | ||||
| -rw-r--r-- | nfc/java/android/nfc/cardemulation/HostApduService.java | 28 | ||||
| -rw-r--r-- | nfc/java/android/nfc/cardemulation/PollingFrame.java | 143 |
4 files changed, 182 insertions, 15 deletions
diff --git a/nfc/api/current.txt b/nfc/api/current.txt index f111327ef619..0383276879e8 100644 --- a/nfc/api/current.txt +++ b/nfc/api/current.txt @@ -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 { diff --git a/nfc/java/android/nfc/NfcAdapter.java b/nfc/java/android/nfc/NfcAdapter.java index c5b758207603..313e6d2c83f4 100644 --- a/nfc/java/android/nfc/NfcAdapter.java +++ b/nfc/java/android/nfc/NfcAdapter.java @@ -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); diff --git a/nfc/java/android/nfc/cardemulation/HostApduService.java b/nfc/java/android/nfc/cardemulation/HostApduService.java index 363788eb5979..a71764deb59a 100644 --- a/nfc/java/android/nfc/cardemulation/HostApduService.java +++ b/nfc/java/android/nfc/cardemulation/HostApduService.java @@ -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) { } /** diff --git a/nfc/java/android/nfc/cardemulation/PollingFrame.java b/nfc/java/android/nfc/cardemulation/PollingFrame.java new file mode 100644 index 000000000000..2e7c77c989dd --- /dev/null +++ b/nfc/java/android/nfc/cardemulation/PollingFrame.java @@ -0,0 +1,143 @@ +/* + * 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; + } +} |