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