• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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.annotation.FlaggedApi;
20 import android.annotation.IntDef;
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.content.ComponentName;
24 import android.os.Bundle;
25 import android.os.Parcel;
26 import android.os.Parcelable;
27 
28 import java.lang.annotation.Retention;
29 import java.lang.annotation.RetentionPolicy;
30 import java.util.HexFormat;
31 import java.util.List;
32 
33 /**
34  * Polling Frames represent data about individual frames of an NFC polling loop. These frames will
35  * be delivered to subclasses of {@link HostApduService} that have registered filters with
36  * {@link CardEmulation#registerPollingLoopFilterForService(ComponentName, String, boolean)} that
37  * match a given frame in a loop and will be delivered through calls to
38  * {@link HostApduService#processPollingFrames(List)}.
39  */
40 public final class PollingFrame implements Parcelable {
41 
42     /**
43      * @hide
44      */
45     @IntDef(prefix = { "POLLING_LOOP_TYPE_"},
46         value = {
47             POLLING_LOOP_TYPE_A,
48             POLLING_LOOP_TYPE_B,
49             POLLING_LOOP_TYPE_F,
50             POLLING_LOOP_TYPE_OFF,
51             POLLING_LOOP_TYPE_ON,
52             POLLING_LOOP_TYPE_UNKNOWN
53         })
54     @Retention(RetentionPolicy.SOURCE)
55     public @interface PollingFrameType {}
56 
57     /**
58      * POLLING_LOOP_TYPE_A is the value associated with the key
59      * POLLING_LOOP_TYPE  in the Bundle passed to {@link HostApduService#processPollingFrames(List)}
60      * when the polling loop is for NFC-A.
61      */
62     public static final int POLLING_LOOP_TYPE_A = 'A';
63 
64     /**
65      * POLLING_LOOP_TYPE_B is the value associated with the key
66      * POLLING_LOOP_TYPE  in the Bundle passed to {@link HostApduService#processPollingFrames(List)}
67      * when the polling loop is for NFC-B.
68      */
69     public static final int POLLING_LOOP_TYPE_B = 'B';
70 
71     /**
72      * POLLING_LOOP_TYPE_F is the value associated with the key
73      * POLLING_LOOP_TYPE  in the Bundle passed to {@link HostApduService#processPollingFrames(List)}
74      * when the polling loop is for NFC-F.
75      */
76     public static final int POLLING_LOOP_TYPE_F = 'F';
77 
78     /**
79      * POLLING_LOOP_TYPE_ON is the value associated with the key
80      * POLLING_LOOP_TYPE  in the Bundle passed to {@link HostApduService#processPollingFrames(List)}
81      * when the polling loop turns on.
82      */
83     public static final int POLLING_LOOP_TYPE_ON = 'O';
84 
85     /**
86      * POLLING_LOOP_TYPE_OFF is the value associated with the key
87      * POLLING_LOOP_TYPE  in the Bundle passed to {@link HostApduService#processPollingFrames(List)}
88      * when the polling loop turns off.
89      */
90     public static final int POLLING_LOOP_TYPE_OFF = 'X';
91 
92     /**
93      * POLLING_LOOP_TYPE_UNKNOWN is the value associated with the key
94      * POLLING_LOOP_TYPE  in the Bundle passed to {@link HostApduService#processPollingFrames(List)}
95      * when the polling loop frame isn't recognized.
96      */
97     public static final int POLLING_LOOP_TYPE_UNKNOWN = 'U';
98 
99     /**
100      * KEY_POLLING_LOOP_TYPE is the Bundle key for the type of
101      * polling loop frame in the Bundle included in MSG_POLLING_LOOP.
102      */
103     private static final String KEY_POLLING_LOOP_TYPE = "android.nfc.cardemulation.TYPE";
104 
105     /**
106      * KEY_POLLING_LOOP_DATA is the Bundle key for the raw data of captured from
107      * the polling loop frame in the Bundle included in MSG_POLLING_LOOP.
108      */
109     private static final String KEY_POLLING_LOOP_DATA = "android.nfc.cardemulation.DATA";
110 
111     /**
112      * KEY_POLLING_LOOP_GAIN is the Bundle key for the field strength of
113      * the polling loop frame in the Bundle included in MSG_POLLING_LOOP.
114     */
115     private static final String KEY_POLLING_LOOP_GAIN = "android.nfc.cardemulation.GAIN";
116 
117     /**
118      * KEY_POLLING_LOOP_TIMESTAMP is the Bundle key for the timestamp of
119      * the polling loop frame in the Bundle included in MSG_POLLING_LOOP.
120     */
121     private static final String KEY_POLLING_LOOP_TIMESTAMP = "android.nfc.cardemulation.TIMESTAMP";
122 
123     /**
124      * KEY_POLLING_LOOP_TIMESTAMP is the Bundle key for whether this polling frame triggered
125      * autoTransact in the Bundle included in MSG_POLLING_LOOP.
126     */
127     private static final String KEY_POLLING_LOOP_TRIGGERED_AUTOTRANSACT =
128             "android.nfc.cardemulation.TRIGGERED_AUTOTRANSACT";
129 
130 
131     @PollingFrameType
132     private final int mType;
133     private final byte[] mData;
134     private final int mGain;
135     private final long mTimestamp;
136     private boolean mTriggeredAutoTransact;
137 
138     public static final @NonNull Parcelable.Creator<PollingFrame> CREATOR =
139             new Parcelable.Creator<>() {
140                 @Override
141                 public PollingFrame createFromParcel(Parcel source) {
142                     return new PollingFrame(source.readBundle());
143                 }
144 
145                 @Override
146                 public PollingFrame[] newArray(int size) {
147                     return new PollingFrame[size];
148                 }
149             };
150 
PollingFrame(Bundle frame)151     private PollingFrame(Bundle frame) {
152         mType = frame.getInt(KEY_POLLING_LOOP_TYPE);
153         byte[] data = frame.getByteArray(KEY_POLLING_LOOP_DATA);
154         mData = (data == null) ? new byte[0] : data;
155         mGain = frame.getInt(KEY_POLLING_LOOP_GAIN, -1);
156         mTimestamp = frame.getLong(KEY_POLLING_LOOP_TIMESTAMP);
157         mTriggeredAutoTransact = frame.containsKey(KEY_POLLING_LOOP_TRIGGERED_AUTOTRANSACT)
158                 && frame.getBoolean(KEY_POLLING_LOOP_TRIGGERED_AUTOTRANSACT);
159     }
160 
161     /**
162      * Constructor for Polling Frames.
163      *
164      * @param type the type of the frame
165      * @param data a byte array of the data contained in the frame
166      * @param gain the vendor-specific gain of the field
167      * @param timestampMicros the timestamp in microseconds
168      * @param triggeredAutoTransact whether or not this frame triggered the device to start a
169      * transaction automatically
170      *
171      * @hide
172      */
PollingFrame(@ollingFrameType int type, @Nullable byte[] data, int gain, long timestampMicros, boolean triggeredAutoTransact)173     public PollingFrame(@PollingFrameType int type, @Nullable byte[] data,
174             int gain, long timestampMicros, boolean triggeredAutoTransact) {
175         mType = type;
176         mData = data == null ? new byte[0] : data;
177         mGain = gain;
178         mTimestamp = timestampMicros;
179         mTriggeredAutoTransact = triggeredAutoTransact;
180     }
181 
182     /**
183      * Returns the type of frame for this polling loop frame.
184      * The possible return values are:
185      * <ul>
186      *   <li>{@link #POLLING_LOOP_TYPE_ON}</li>
187      *   <li>{@link #POLLING_LOOP_TYPE_OFF}</li>
188      *   <li>{@link #POLLING_LOOP_TYPE_A}</li>
189      *   <li>{@link #POLLING_LOOP_TYPE_B}</li>
190      *   <li>{@link #POLLING_LOOP_TYPE_F}</li>
191      * </ul>
192      */
getType()193     public @PollingFrameType int getType() {
194         return mType;
195     }
196 
197     /**
198      * Returns the raw data from the polling type frame.
199      */
getData()200     public @NonNull byte[] getData() {
201         return mData;
202     }
203 
204     /**
205      * Returns the gain representing the field strength of the NFC field when this polling loop
206      * frame was observed.
207      * @return the gain or -1 if there is no gain measurement associated with this frame.
208      */
getVendorSpecificGain()209     public int getVendorSpecificGain() {
210         return mGain;
211     }
212 
213     /**
214      * Returns the timestamp of when the polling loop frame was observed, in microseconds. These
215      * timestamps are relative and should only be used for comparing the timing of frames relative
216      * to each other.
217      * @return the timestamp in microseconds
218      */
getTimestamp()219     public long getTimestamp() {
220         return mTimestamp;
221     }
222 
223     /**
224      * @hide
225      */
setTriggeredAutoTransact(boolean triggeredAutoTransact)226     public void setTriggeredAutoTransact(boolean triggeredAutoTransact) {
227         mTriggeredAutoTransact = triggeredAutoTransact;
228     }
229 
230     /**
231      * Returns whether this frame triggered the device to automatically disable observe mode and
232      * allow one transaction.
233      */
getTriggeredAutoTransact()234     public boolean getTriggeredAutoTransact() {
235         return mTriggeredAutoTransact;
236     }
237 
238     @Override
describeContents()239     public int describeContents() {
240         return 0;
241     }
242 
243     @Override
writeToParcel(@onNull Parcel dest, int flags)244     public void writeToParcel(@NonNull Parcel dest, int flags) {
245         dest.writeBundle(toBundle());
246     }
247 
248     /**
249      * @return a Bundle representing this frame
250      */
toBundle()251     private Bundle toBundle() {
252         Bundle frame = new Bundle();
253         frame.putInt(KEY_POLLING_LOOP_TYPE, getType());
254         if (getVendorSpecificGain() != -1) {
255             frame.putInt(KEY_POLLING_LOOP_GAIN, (byte) getVendorSpecificGain());
256         }
257         frame.putByteArray(KEY_POLLING_LOOP_DATA, getData());
258         frame.putLong(KEY_POLLING_LOOP_TIMESTAMP, getTimestamp());
259         frame.putBoolean(KEY_POLLING_LOOP_TRIGGERED_AUTOTRANSACT, getTriggeredAutoTransact());
260         return frame;
261     }
262 
263     @Override
toString()264     public String toString() {
265         return "PollingFrame { Type: " + (char) getType()
266                 + ", gain: " + getVendorSpecificGain()
267                 + ", timestamp: " + Long.toUnsignedString(getTimestamp())
268                 + ", data: [" + HexFormat.ofDelimiter(" ").formatHex(getData()) + "] }";
269     }
270 }
271