blob: 4f3e1999f5d301514e0ae3beffe0bcfff1476a24 [file] [log] [blame]
/*
* Copyright 2024 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;
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.time.Instant;
/**
* A log class for OEMs to get log information of NFC events.
* @hide
*/
@FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
@SystemApi
public final class OemLogItems implements Parcelable {
/**
* Used when RF field state is changed.
*/
public static final int LOG_ACTION_RF_FIELD_STATE_CHANGED = 0X01;
/**
* Used when NFC is toggled. Event should be set to {@link LogEvent#EVENT_ENABLE} or
* {@link LogEvent#EVENT_DISABLE} if this action is used.
*/
public static final int LOG_ACTION_NFC_TOGGLE = 0x0201;
/**
* Used when sending host routing status.
*/
public static final int LOG_ACTION_HCE_DATA = 0x0204;
/**
* Used when screen state is changed.
*/
public static final int LOG_ACTION_SCREEN_STATE_CHANGED = 0x0206;
/**
* Used when tag is detected.
*/
public static final int LOG_ACTION_TAG_DETECTED = 0x03;
/**
* @hide
*/
@IntDef(prefix = { "LOG_ACTION_" }, value = {
LOG_ACTION_RF_FIELD_STATE_CHANGED,
LOG_ACTION_NFC_TOGGLE,
LOG_ACTION_HCE_DATA,
LOG_ACTION_SCREEN_STATE_CHANGED,
LOG_ACTION_TAG_DETECTED,
})
@Retention(RetentionPolicy.SOURCE)
public @interface LogAction {}
/**
* Represents the event is not set.
*/
public static final int EVENT_UNSET = 0;
/**
* Represents nfc enable is called.
*/
public static final int EVENT_ENABLE = 1;
/**
* Represents nfc disable is called.
*/
public static final int EVENT_DISABLE = 2;
/** @hide */
@IntDef(prefix = { "EVENT_" }, value = {
EVENT_UNSET,
EVENT_ENABLE,
EVENT_DISABLE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface LogEvent {}
private int mAction;
private int mEvent;
private int mCallingPid;
private byte[] mCommandApdus;
private byte[] mResponseApdus;
private Instant mRfFieldOnTime;
private Tag mTag;
/** @hide */
public OemLogItems(@LogAction int action, @LogEvent int event, int callingPid,
byte[] commandApdus, byte[] responseApdus, Instant rfFieldOnTime,
Tag tag) {
mAction = action;
mEvent = event;
mTag = tag;
mCallingPid = callingPid;
mCommandApdus = commandApdus;
mResponseApdus = responseApdus;
mRfFieldOnTime = rfFieldOnTime;
}
/**
* Describe the kinds of special objects contained in this Parcelable
* instance's marshaled representation. For example, if the object will
* include a file descriptor in the output of {@link #writeToParcel(Parcel, int)},
* the return value of this method must include the
* {@link #CONTENTS_FILE_DESCRIPTOR} bit.
*
* @return a bitmask indicating the set of special object types marshaled
* by this Parcelable object instance.
*/
@Override
public int describeContents() {
return 0;
}
/**
* Flatten this object in to a Parcel.
*
* @param dest The Parcel in which the object should be written.
* @param flags Additional flags about how the object should be written.
* May be 0 or {@link #PARCELABLE_WRITE_RETURN_VALUE}.
*/
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(mAction);
dest.writeInt(mEvent);
dest.writeInt(mCallingPid);
dest.writeInt(mCommandApdus.length);
dest.writeByteArray(mCommandApdus);
dest.writeInt(mResponseApdus.length);
dest.writeByteArray(mResponseApdus);
dest.writeBoolean(mRfFieldOnTime != null);
if (mRfFieldOnTime != null) {
dest.writeLong(mRfFieldOnTime.getEpochSecond());
dest.writeInt(mRfFieldOnTime.getNano());
}
dest.writeParcelable(mTag, 0);
}
/** @hide */
public static class Builder {
private final OemLogItems mItem;
public Builder(@LogAction int type) {
mItem = new OemLogItems(type, EVENT_UNSET, 0, new byte[0], new byte[0], null, null);
}
/** Setter of the log action. */
public OemLogItems.Builder setAction(@LogAction int action) {
mItem.mAction = action;
return this;
}
/** Setter of the log calling event. */
public OemLogItems.Builder setCallingEvent(@LogEvent int event) {
mItem.mEvent = event;
return this;
}
/** Setter of the log calling Pid. */
public OemLogItems.Builder setCallingPid(int pid) {
mItem.mCallingPid = pid;
return this;
}
/** Setter of APDU command. */
public OemLogItems.Builder setApduCommand(byte[] apdus) {
mItem.mCommandApdus = apdus;
return this;
}
/** Setter of RF field on time. */
public OemLogItems.Builder setRfFieldOnTime(Instant time) {
mItem.mRfFieldOnTime = time;
return this;
}
/** Setter of APDU response. */
public OemLogItems.Builder setApduResponse(byte[] apdus) {
mItem.mResponseApdus = apdus;
return this;
}
/** Setter of dispatched tag. */
public OemLogItems.Builder setTag(Tag tag) {
mItem.mTag = tag;
return this;
}
/** Builds an {@link OemLogItems} instance. */
public OemLogItems build() {
return mItem;
}
}
/**
* Gets the action of this log.
* @return one of {@link LogAction}
*/
@LogAction
public int getAction() {
return mAction;
}
/**
* Gets the event of this log. This will be set to {@link LogEvent#EVENT_ENABLE} or
* {@link LogEvent#EVENT_DISABLE} only when action is set to
* {@link LogAction#LOG_ACTION_NFC_TOGGLE}
* @return one of {@link LogEvent}
*/
@LogEvent
public int getEvent() {
return mEvent;
}
/**
* Gets the calling Pid of this log. This field will be set only when action is set to
* {@link LogAction#LOG_ACTION_NFC_TOGGLE}
* @return calling Pid
*/
public int getCallingPid() {
return mCallingPid;
}
/**
* Gets the command APDUs of this log. This field will be set only when action is set to
* {@link LogAction#LOG_ACTION_HCE_DATA}
* @return a byte array of command APDUs with the same format as
* {@link android.nfc.cardemulation.HostApduService#sendResponseApdu(byte[])}
*/
@Nullable
public byte[] getCommandApdu() {
return mCommandApdus;
}
/**
* Gets the response APDUs of this log. This field will be set only when action is set to
* {@link LogAction#LOG_ACTION_HCE_DATA}
* @return a byte array of response APDUs with the same format as
* {@link android.nfc.cardemulation.HostApduService#sendResponseApdu(byte[])}
*/
@Nullable
public byte[] getResponseApdu() {
return mResponseApdus;
}
/**
* Gets the RF field event time in this log in millisecond. This field will be set only when
* action is set to {@link LogAction#LOG_ACTION_RF_FIELD_STATE_CHANGED}
* @return an {@link Instant} of RF field event time.
*/
@Nullable
public Instant getRfFieldEventTimeMillis() {
return mRfFieldOnTime;
}
/**
* Gets the tag of this log. This field will be set only when action is set to
* {@link LogAction#LOG_ACTION_TAG_DETECTED}
* @return a detected {@link Tag} in {@link #LOG_ACTION_TAG_DETECTED} case. Return
* null otherwise.
*/
@Nullable
public Tag getTag() {
return mTag;
}
private String byteToHex(byte[] bytes) {
char[] HexArray = "0123456789ABCDEF".toCharArray();
char[] hexChars = new char[bytes.length * 2];
for (int j = 0; j < bytes.length; j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = HexArray[v >>> 4];
hexChars[j * 2 + 1] = HexArray[v & 0x0F];
}
return new String(hexChars);
}
@Override
public String toString() {
return "[mCommandApdus: "
+ ((mCommandApdus != null) ? byteToHex(mCommandApdus) : "null")
+ "[mResponseApdus: "
+ ((mResponseApdus != null) ? byteToHex(mResponseApdus) : "null")
+ ", mCallingApi= " + mEvent
+ ", mAction= " + mAction
+ ", mCallingPId = " + mCallingPid
+ ", mRfFieldOnTime= " + mRfFieldOnTime;
}
private OemLogItems(Parcel in) {
this.mAction = in.readInt();
this.mEvent = in.readInt();
this.mCallingPid = in.readInt();
this.mCommandApdus = new byte[in.readInt()];
in.readByteArray(this.mCommandApdus);
this.mResponseApdus = new byte[in.readInt()];
in.readByteArray(this.mResponseApdus);
boolean isRfFieldOnTimeSet = in.readBoolean();
if (isRfFieldOnTimeSet) {
this.mRfFieldOnTime = Instant.ofEpochSecond(in.readLong(), in.readInt());
} else {
this.mRfFieldOnTime = null;
}
this.mTag = in.readParcelable(Tag.class.getClassLoader(), Tag.class);
}
public static final @NonNull Parcelable.Creator<OemLogItems> CREATOR =
new Parcelable.Creator<OemLogItems>() {
@Override
public OemLogItems createFromParcel(Parcel in) {
return new OemLogItems(in);
}
@Override
public OemLogItems[] newArray(int size) {
return new OemLogItems[size];
}
};
}