diff options
307 files changed, 4233 insertions, 21305 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index 7c56a5811abb..d0b3a51f998e 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -20781,11 +20781,11 @@ package android.hardware.display { method @FlaggedApi("com.android.server.display.feature.flags.display_listener_performance_improvements") public void registerDisplayListener(@NonNull java.util.concurrent.Executor, long, @NonNull android.hardware.display.DisplayManager.DisplayListener); method public void unregisterDisplayListener(android.hardware.display.DisplayManager.DisplayListener); field public static final String DISPLAY_CATEGORY_PRESENTATION = "android.hardware.display.category.PRESENTATION"; - field @FlaggedApi("com.android.server.display.feature.flags.display_listener_performance_improvements") public static final long EVENT_FLAG_DISPLAY_ADDED = 1L; // 0x1L - field @FlaggedApi("com.android.server.display.feature.flags.display_listener_performance_improvements") public static final long EVENT_FLAG_DISPLAY_CHANGED = 4L; // 0x4L - field @FlaggedApi("com.android.server.display.feature.flags.display_listener_performance_improvements") public static final long EVENT_FLAG_DISPLAY_REFRESH_RATE = 8L; // 0x8L - field @FlaggedApi("com.android.server.display.feature.flags.display_listener_performance_improvements") public static final long EVENT_FLAG_DISPLAY_REMOVED = 2L; // 0x2L - field @FlaggedApi("com.android.server.display.feature.flags.display_listener_performance_improvements") public static final long EVENT_FLAG_DISPLAY_STATE = 16L; // 0x10L + field @FlaggedApi("com.android.server.display.feature.flags.display_listener_performance_improvements") public static final long EVENT_TYPE_DISPLAY_ADDED = 1L; // 0x1L + field @FlaggedApi("com.android.server.display.feature.flags.display_listener_performance_improvements") public static final long EVENT_TYPE_DISPLAY_CHANGED = 4L; // 0x4L + field @FlaggedApi("com.android.server.display.feature.flags.display_listener_performance_improvements") public static final long EVENT_TYPE_DISPLAY_REFRESH_RATE = 8L; // 0x8L + field @FlaggedApi("com.android.server.display.feature.flags.display_listener_performance_improvements") public static final long EVENT_TYPE_DISPLAY_REMOVED = 2L; // 0x2L + field @FlaggedApi("com.android.server.display.feature.flags.display_listener_performance_improvements") public static final long EVENT_TYPE_DISPLAY_STATE = 16L; // 0x10L field public static final int MATCH_CONTENT_FRAMERATE_ALWAYS = 2; // 0x2 field public static final int MATCH_CONTENT_FRAMERATE_NEVER = 0; // 0x0 field public static final int MATCH_CONTENT_FRAMERATE_SEAMLESSS_ONLY = 1; // 0x1 diff --git a/core/api/system-current.txt b/core/api/system-current.txt index ff5b2bda7ccf..a60fd117fd17 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -5157,18 +5157,18 @@ package android.hardware.contexthub { } @FlaggedApi("android.chre.flags.offload_api") public final class HubMessage implements android.os.Parcelable { - ctor public HubMessage(int, @NonNull byte[]); - ctor public HubMessage(int, @NonNull byte[], @NonNull android.hardware.contexthub.HubMessage.DeliveryParams); method public int describeContents(); method @NonNull public byte[] getMessageBody(); method public int getMessageType(); + method public boolean isResponseRequired(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.hardware.contexthub.HubMessage> CREATOR; } - public static class HubMessage.DeliveryParams { - ctor public HubMessage.DeliveryParams(boolean); - method public boolean isResponseRequired(); + public static final class HubMessage.Builder { + ctor public HubMessage.Builder(int, @NonNull byte[]); + method @NonNull public android.hardware.contexthub.HubMessage build(); + method @NonNull public android.hardware.contexthub.HubMessage.Builder setResponseRequired(boolean); } @FlaggedApi("android.chre.flags.offload_api") public final class HubServiceInfo implements android.os.Parcelable { diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index eeb1ebb69b03..8ffea237c71d 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -6453,6 +6453,13 @@ public class Notification implements Parcelable big.setColorStateList(R.id.snooze_button, "setImageTintList", actionColor); big.setColorStateList(R.id.bubble_button, "setImageTintList", actionColor); + if (Flags.notificationsRedesignTemplates()) { + int margin = getContentMarginTop(mContext, + R.dimen.notification_2025_content_margin_top); + big.setViewLayoutMargin(R.id.notification_main_column, RemoteViews.MARGIN_TOP, + margin, TypedValue.COMPLEX_UNIT_PX); + } + boolean validRemoteInput = false; // In the UI, contextual actions appear separately from the standard actions, so we @@ -6549,6 +6556,30 @@ public class Notification implements Parcelable return big; } + /** + * Calculate the top margin for the content in px, to allow enough space for the top line + * above, using the given resource ID for the desired spacing. + * + * @hide + */ + public static int getContentMarginTop(Context context, @DimenRes int spacingRes) { + final Resources resources = context.getResources(); + // The margin above the text, at the top of the notification (originally in dp) + int notifMargin = resources.getDimensionPixelSize(R.dimen.notification_2025_margin); + // Spacing between the text lines, scaling with the font size (originally in sp) + int spacing = resources.getDimensionPixelSize(spacingRes); + + // Size of the text in the notification top line (originally in sp) + int[] textSizeAttr = new int[] { android.R.attr.textSize }; + TypedArray typedArray = context.obtainStyledAttributes( + R.style.TextAppearance_DeviceDefault_Notification_Info, textSizeAttr); + int textSize = typedArray.getDimensionPixelSize(0 /* index */, -1 /* default */); + typedArray.recycle(); + + // Adding up all the values as pixels + return notifMargin + spacing + textSize; + } + private boolean hasValidRemoteInput(Action action) { if (TextUtils.isEmpty(action.title) || action.actionIntent == null) { // Weird actions diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 39c27a165588..84d67415a4b4 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -17430,12 +17430,17 @@ public class DevicePolicyManager { } /** - * Removes a manged profile from the device only when called from a managed profile's context + * Removes a managed profile from the device. + * + * <p> + * Removes the managed profile which is specified by the context user + * ({@code Context.createContextAsUser()}). + * <p> * - * @param user UserHandle of the profile to be removed * @return {@code true} when removal of managed profile was successful, {@code false} when - * removal was unsuccessful or throws IllegalArgumentException when provided user was not a + * removal was unsuccessful or throws IllegalArgumentException when specified user was not a * managed profile + * * @hide */ @SystemApi diff --git a/core/java/android/app/appfunctions/SafeOneTimeExecuteAppFunctionCallback.java b/core/java/android/app/appfunctions/SafeOneTimeExecuteAppFunctionCallback.java index e527de209a75..88001fc6cc3c 100644 --- a/core/java/android/app/appfunctions/SafeOneTimeExecuteAppFunctionCallback.java +++ b/core/java/android/app/appfunctions/SafeOneTimeExecuteAppFunctionCallback.java @@ -19,7 +19,6 @@ package android.app.appfunctions; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.RemoteException; -import android.os.SystemClock; import android.util.Log; import java.util.Objects; @@ -40,8 +39,7 @@ public class SafeOneTimeExecuteAppFunctionCallback { @NonNull private final IExecuteAppFunctionCallback mCallback; - @Nullable - private final CompletionCallback mCompletionCallback; + @Nullable private final CompletionCallback mCompletionCallback; private final AtomicLong mExecutionStartTimeAfterBindMillis = new AtomicLong(); @@ -49,7 +47,8 @@ public class SafeOneTimeExecuteAppFunctionCallback { this(callback, /* completionCallback= */ null); } - public SafeOneTimeExecuteAppFunctionCallback(@NonNull IExecuteAppFunctionCallback callback, + public SafeOneTimeExecuteAppFunctionCallback( + @NonNull IExecuteAppFunctionCallback callback, @Nullable CompletionCallback completionCallback) { mCallback = Objects.requireNonNull(callback); mCompletionCallback = completionCallback; @@ -64,8 +63,8 @@ public class SafeOneTimeExecuteAppFunctionCallback { try { mCallback.onSuccess(result); if (mCompletionCallback != null) { - mCompletionCallback.finalizeOnSuccess(result, - mExecutionStartTimeAfterBindMillis.get()); + mCompletionCallback.finalizeOnSuccess( + result, mExecutionStartTimeAfterBindMillis.get()); } } catch (RemoteException ex) { // Failed to notify the other end. Ignore. @@ -82,8 +81,8 @@ public class SafeOneTimeExecuteAppFunctionCallback { try { mCallback.onError(error); if (mCompletionCallback != null) { - mCompletionCallback.finalizeOnError(error, - mExecutionStartTimeAfterBindMillis.get()); + mCompletionCallback.finalizeOnError( + error, mExecutionStartTimeAfterBindMillis.get()); } } catch (RemoteException ex) { // Failed to notify the other end. Ignore. @@ -103,9 +102,10 @@ public class SafeOneTimeExecuteAppFunctionCallback { * Sets the execution start time of the request. Used to calculate the overhead latency of * requests. */ - public void setExecutionStartTimeMillis() { - if (!mExecutionStartTimeAfterBindMillis.compareAndSet(0, SystemClock.elapsedRealtime())) { - Log.w(TAG, "Ignore subsequent calls to setExecutionStartTimeMillis()"); + public void setExecutionStartTimeAfterBindMillis(long executionStartTimeAfterBindMillis) { + if (!mExecutionStartTimeAfterBindMillis.compareAndSet( + 0, executionStartTimeAfterBindMillis)) { + Log.w(TAG, "Ignore subsequent calls to setExecutionStartTimeAfterBindMillis()"); } } @@ -115,8 +115,8 @@ public class SafeOneTimeExecuteAppFunctionCallback { */ public interface CompletionCallback { /** Called after {@link IExecuteAppFunctionCallback#onSuccess}. */ - void finalizeOnSuccess(@NonNull ExecuteAppFunctionResponse result, - long executionStartTimeMillis); + void finalizeOnSuccess( + @NonNull ExecuteAppFunctionResponse result, long executionStartTimeMillis); /** Called after {@link IExecuteAppFunctionCallback#onError}. */ void finalizeOnError(@NonNull AppFunctionException error, long executionStartTimeMillis); diff --git a/core/java/android/credentials/flags.aconfig b/core/java/android/credentials/flags.aconfig index 9c811fb84da3..2161e10337c9 100644 --- a/core/java/android/credentials/flags.aconfig +++ b/core/java/android/credentials/flags.aconfig @@ -13,6 +13,16 @@ flag { flag { namespace: "credential_manager" + name: "package_update_fix_enabled" + description: "Enable fix for removing package from settings if app is updated or component is modified" + bug: "384772470" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { + namespace: "credential_manager" name: "settings_activity_enabled" is_exported: true description: "Enable the Credential Manager Settings Activity APIs" diff --git a/core/java/android/hardware/contexthub/HubEndpoint.java b/core/java/android/hardware/contexthub/HubEndpoint.java index af92cffc57f4..6c669a3d7032 100644 --- a/core/java/android/hardware/contexthub/HubEndpoint.java +++ b/core/java/android/hardware/contexthub/HubEndpoint.java @@ -296,7 +296,7 @@ public class HubEndpoint { } if (activeSession == null || mMessageCallback == null) { - if (message.getDeliveryParams().isResponseRequired()) { + if (message.isResponseRequired()) { try { mServiceToken.sendMessageDeliveryStatus( sessionId, @@ -313,7 +313,7 @@ public class HubEndpoint { mMessageCallbackExecutor.execute( () -> { mMessageCallback.onMessageReceived(activeSession, message); - if (message.getDeliveryParams().isResponseRequired()) { + if (message.isResponseRequired()) { try { mServiceToken.sendMessageDeliveryStatus( sessionId, diff --git a/core/java/android/hardware/contexthub/HubEndpointSession.java b/core/java/android/hardware/contexthub/HubEndpointSession.java index f7f5636264e4..dd6e52f51df0 100644 --- a/core/java/android/hardware/contexthub/HubEndpointSession.java +++ b/core/java/android/hardware/contexthub/HubEndpointSession.java @@ -79,7 +79,7 @@ public class HubEndpointSession implements AutoCloseable { throw new IllegalStateException("Session is already closed."); } - boolean isResponseRequired = message.getDeliveryParams().isResponseRequired(); + boolean isResponseRequired = message.isResponseRequired(); ContextHubTransaction<Void> ret = new ContextHubTransaction<>( isResponseRequired diff --git a/core/java/android/hardware/contexthub/HubMessage.java b/core/java/android/hardware/contexthub/HubMessage.java index 6373bbad9f25..8a8617d6c70d 100644 --- a/core/java/android/hardware/contexthub/HubMessage.java +++ b/core/java/android/hardware/contexthub/HubMessage.java @@ -29,7 +29,8 @@ import java.util.Arrays; import java.util.Objects; /** - * A class describing general messages send through the Context Hub Service. + * A class describing general messages send through the Context Hub Service through {@link + * HubEndpointSession#sendMessage}. * * @hide */ @@ -41,84 +42,14 @@ public final class HubMessage implements Parcelable { private final int mMessageType; private final byte[] mMessageBody; - private final DeliveryParams mDeliveryParams; + private final boolean mResponseRequired; private int mMessageSequenceNumber; - /** - * Configurable options for message delivery. This option can be passed into {@link - * HubEndpointSession#sendMessage} to specify the behavior of message delivery. - */ - public static class DeliveryParams { - private final boolean mResponseRequired; - - /** - * @param responseRequired If true, message sent with this option will have a {@link - * android.hardware.location.ContextHubTransaction.Response} when the peer received the - * message. - */ - public DeliveryParams(boolean responseRequired) { - mResponseRequired = responseRequired; - } - - /** Get the acknowledgement requirement. */ - public boolean isResponseRequired() { - return mResponseRequired; - } - - @Override - public String toString() { - StringBuilder out = new StringBuilder(); - out.append("DeliveryParams["); - out.append("responseRequired = ").append(mResponseRequired); - out.append("]"); - return out.toString(); - } - - @Override - public int hashCode() { - return Objects.hash(mResponseRequired); - } - - @Override - public boolean equals(Object obj) { - if (obj == this) { - return true; - } - - if (obj instanceof DeliveryParams other) { - return other.mResponseRequired == mResponseRequired; - } - - return false; - } - } - - /** - * Default constructor for HubMessage with no response required. - * - * @param messageType the endpoint & service dependent message type - * @param messageBody the byte array message contents - */ - public HubMessage(int messageType, @NonNull byte[] messageBody) { + private HubMessage(int messageType, @NonNull byte[] messageBody, boolean responseRequired) { Objects.requireNonNull(messageBody, "messageBody cannot be null"); mMessageType = messageType; mMessageBody = messageBody; - mDeliveryParams = new DeliveryParams(/* responseRequired= */ false); - } - - /** - * @param messageType the endpoint & service dependent message type - * @param messageBody the byte array message contents - * @param deliveryParams The message delivery parameters. See {@link HubMessage.DeliveryParams} - * for more details. - */ - public HubMessage( - int messageType, @NonNull byte[] messageBody, @NonNull DeliveryParams deliveryParams) { - Objects.requireNonNull(messageBody, "messageBody cannot be null"); - Objects.requireNonNull(deliveryParams, "deliveryParams cannot be null"); - mMessageType = messageType; - mMessageBody = messageBody; - mDeliveryParams = deliveryParams; + mResponseRequired = responseRequired; } /** @@ -141,12 +72,10 @@ public final class HubMessage implements Parcelable { } /** - * Retrieve the {@link DeliveryParams} object specifying the behavior of message delivery. - * - * @hide + * @return true if a response is required when the peer endpoint receives the message. */ - public DeliveryParams getDeliveryParams() { - return mDeliveryParams; + public boolean isResponseRequired() { + return mResponseRequired; } /** @@ -175,7 +104,7 @@ public final class HubMessage implements Parcelable { mMessageBody = new byte[msgSize]; in.readByteArray(mMessageBody); - mDeliveryParams = new DeliveryParams(in.readInt() == 1); + mResponseRequired = (in.readInt() == 1); mMessageSequenceNumber = in.readInt(); } @@ -191,7 +120,7 @@ public final class HubMessage implements Parcelable { out.writeInt(mMessageBody.length); out.writeByteArray(mMessageBody); - out.writeInt(mDeliveryParams.isResponseRequired() ? 1 : 0); + out.writeInt(mResponseRequired ? 1 : 0); out.writeInt(mMessageSequenceNumber); } @@ -217,7 +146,7 @@ public final class HubMessage implements Parcelable { out.append("HubMessage[type = ").append(mMessageType); out.append(", length = ").append(mMessageBody.length); out.append(", messageSequenceNumber = ").append(mMessageSequenceNumber); - out.append(", deliveryParams = ").append(mDeliveryParams); + out.append(", responseRequired = ").append(mResponseRequired); out.append("]("); if (length > 0) { @@ -249,7 +178,7 @@ public final class HubMessage implements Parcelable { isEqual = (other.getMessageType() == mMessageType) && Arrays.equals(other.getMessageBody(), mMessageBody) - && (other.getDeliveryParams().equals(mDeliveryParams)) + && (other.isResponseRequired() == mResponseRequired) && (other.getMessageSequenceNumber() == mMessageSequenceNumber); } @@ -265,7 +194,41 @@ public final class HubMessage implements Parcelable { return Objects.hash( mMessageType, Arrays.hashCode(mMessageBody), - mDeliveryParams, + mResponseRequired, mMessageSequenceNumber); } + + public static final class Builder { + private int mMessageType; + private byte[] mMessageBody; + private boolean mResponseRequired = false; + + /** + * Create a builder for {@link HubMessage} with a default delivery parameters. + * + * @param messageType the endpoint & service dependent message type + * @param messageBody the byte array message contents + */ + public Builder(int messageType, @NonNull byte[] messageBody) { + mMessageType = messageType; + mMessageBody = messageBody; + } + + /** + * @param responseRequired If true, message sent with this option will have a {@link + * android.hardware.location.ContextHubTransaction.Response} when the peer received the + * message. Default is false. + */ + @NonNull + public Builder setResponseRequired(boolean responseRequired) { + mResponseRequired = responseRequired; + return this; + } + + /** Build the {@link HubMessage} object. */ + @NonNull + public HubMessage build() { + return new HubMessage(mMessageType, mMessageBody, mResponseRequired); + } + } } diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index 7054c37cbc3b..6716598f9e4c 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -578,73 +578,82 @@ public final class DisplayManager { /** * @hide */ - @LongDef(flag = true, prefix = {"EVENT_FLAG_"}, value = { - EVENT_FLAG_DISPLAY_ADDED, - EVENT_FLAG_DISPLAY_CHANGED, - EVENT_FLAG_DISPLAY_REMOVED, - EVENT_FLAG_DISPLAY_REFRESH_RATE, - EVENT_FLAG_DISPLAY_STATE + @LongDef(flag = true, prefix = {"EVENT_TYPE_"}, value = { + EVENT_TYPE_DISPLAY_ADDED, + EVENT_TYPE_DISPLAY_CHANGED, + EVENT_TYPE_DISPLAY_REMOVED, + EVENT_TYPE_DISPLAY_REFRESH_RATE, + EVENT_TYPE_DISPLAY_STATE }) @Retention(RetentionPolicy.SOURCE) - public @interface EventFlag {} + public @interface EventType {} /** * @hide */ - @LongDef(flag = true, prefix = {"PRIVATE_EVENT_FLAG_"}, value = { - PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS, - PRIVATE_EVENT_FLAG_HDR_SDR_RATIO_CHANGED, - PRIVATE_EVENT_FLAG_DISPLAY_CONNECTION_CHANGED, + @LongDef(flag = true, prefix = {"PRIVATE_EVENT_TYPE_"}, value = { + PRIVATE_EVENT_TYPE_DISPLAY_BRIGHTNESS, + PRIVATE_EVENT_TYPE_HDR_SDR_RATIO_CHANGED, + PRIVATE_EVENT_TYPE_DISPLAY_CONNECTION_CHANGED, }) @Retention(RetentionPolicy.SOURCE) - public @interface PrivateEventFlag {} + public @interface PrivateEventType {} /** - * Event type for when a new display is added. + * Event type for when a new display is added. This notification is sent + * through the {@link DisplayListener#onDisplayAdded} callback method * * @see #registerDisplayListener(DisplayListener, Handler, long) * */ @FlaggedApi(FLAG_DISPLAY_LISTENER_PERFORMANCE_IMPROVEMENTS) - public static final long EVENT_FLAG_DISPLAY_ADDED = 1L << 0; + public static final long EVENT_TYPE_DISPLAY_ADDED = 1L << 0; /** - * Event type for when a display is removed. + * Event type for when a display is removed. This notification is sent + * through the {@link DisplayListener#onDisplayRemoved} callback method * * @see #registerDisplayListener(DisplayListener, Handler, long) * */ @FlaggedApi(FLAG_DISPLAY_LISTENER_PERFORMANCE_IMPROVEMENTS) - public static final long EVENT_FLAG_DISPLAY_REMOVED = 1L << 1; + public static final long EVENT_TYPE_DISPLAY_REMOVED = 1L << 1; /** - * Event type for when a display is changed. + * Event type for when a display is changed. {@link DisplayListener#onDisplayChanged} callback + * is triggered whenever the properties of a {@link android.view.Display}, such as size, + * state, density are modified. * * @see #registerDisplayListener(DisplayListener, Handler, long) * */ @FlaggedApi(FLAG_DISPLAY_LISTENER_PERFORMANCE_IMPROVEMENTS) - public static final long EVENT_FLAG_DISPLAY_CHANGED = 1L << 2; - + public static final long EVENT_TYPE_DISPLAY_CHANGED = 1L << 2; /** - * Event flag to register for a display's refresh rate changes. + * Event type for when a display's refresh rate changes. + * {@link DisplayListener#onDisplayChanged} callback is triggered whenever the refresh rate + * of the display changes. New refresh rate values can be retrieved via + * {@link Display#getRefreshRate()}. * * @see #registerDisplayListener(DisplayListener, Handler, long) */ @FlaggedApi(FLAG_DISPLAY_LISTENER_PERFORMANCE_IMPROVEMENTS) - public static final long EVENT_FLAG_DISPLAY_REFRESH_RATE = 1L << 3; + public static final long EVENT_TYPE_DISPLAY_REFRESH_RATE = 1L << 3; /** - * Event flag to register for a display state changes. + * Event type for when a display state changes. + * {@link DisplayListener#onDisplayChanged} callback is triggered whenever the state + * of the display changes. New state values can be retrieved via + * {@link Display#getState()}. * * @see #registerDisplayListener(DisplayListener, Handler, long) */ @FlaggedApi(FLAG_DISPLAY_LISTENER_PERFORMANCE_IMPROVEMENTS) - public static final long EVENT_FLAG_DISPLAY_STATE = 1L << 4; + public static final long EVENT_TYPE_DISPLAY_STATE = 1L << 4; /** - * Event flag to register for a display's brightness changes. This notification is sent + * Event type to register for a display's brightness changes. This notification is sent * through the {@link DisplayListener#onDisplayChanged} callback method. New brightness * values can be retrieved via {@link android.view.Display#getBrightnessInfo()}. * @@ -652,10 +661,10 @@ public final class DisplayManager { * * @hide */ - public static final long PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS = 1L << 0; + public static final long PRIVATE_EVENT_TYPE_DISPLAY_BRIGHTNESS = 1L << 0; /** - * Event flag to register for a display's hdr/sdr ratio changes. This notification is sent + * Event type to register for a display's hdr/sdr ratio changes. This notification is sent * through the {@link DisplayListener#onDisplayChanged} callback method. New hdr/sdr * values can be retrieved via {@link Display#getHdrSdrRatio()}. * @@ -665,15 +674,15 @@ public final class DisplayManager { * * @hide */ - public static final long PRIVATE_EVENT_FLAG_HDR_SDR_RATIO_CHANGED = 1L << 1; + public static final long PRIVATE_EVENT_TYPE_HDR_SDR_RATIO_CHANGED = 1L << 1; /** - * Event flag to register for a display's connection changed. + * Event type to register for a display's connection changed. * * @see #registerDisplayListener(DisplayListener, Handler, long) * @hide */ - public static final long PRIVATE_EVENT_FLAG_DISPLAY_CONNECTION_CHANGED = 1L << 2; + public static final long PRIVATE_EVENT_TYPE_DISPLAY_CONNECTION_CHANGED = 1L << 2; /** @hide */ @@ -800,8 +809,8 @@ public final class DisplayManager { * @see #unregisterDisplayListener */ public void registerDisplayListener(DisplayListener listener, Handler handler) { - registerDisplayListener(listener, handler, EVENT_FLAG_DISPLAY_ADDED - | EVENT_FLAG_DISPLAY_CHANGED | EVENT_FLAG_DISPLAY_REMOVED); + registerDisplayListener(listener, handler, EVENT_TYPE_DISPLAY_ADDED + | EVENT_TYPE_DISPLAY_CHANGED | EVENT_TYPE_DISPLAY_REMOVED); } /** @@ -810,7 +819,7 @@ public final class DisplayManager { * @param listener The listener to register. * @param handler The handler on which the listener should be invoked, or null * if the listener should be invoked on the calling thread's looper. - * @param eventFlags A bitmask of the event types for which this listener is subscribed. + * @param eventFilter A bitmask of the event types for which this listener is subscribed. * * @see #registerDisplayListener(DisplayListener, Handler) * @see #unregisterDisplayListener @@ -818,9 +827,9 @@ public final class DisplayManager { * @hide */ public void registerDisplayListener(@NonNull DisplayListener listener, - @Nullable Handler handler, @EventFlag long eventFlags) { + @Nullable Handler handler, @EventType long eventFilter) { mGlobal.registerDisplayListener(listener, handler, - mGlobal.mapFlagsToInternalEventFlag(eventFlags, 0), + mGlobal.mapFiltersToInternalEventFlag(eventFilter, 0), ActivityThread.currentPackageName()); } @@ -829,17 +838,17 @@ public final class DisplayManager { * * @param listener The listener to register. * @param executor Executor for the thread that will be receiving the callbacks. Cannot be null. - * @param eventFlags A bitmask of the event types for which this listener is subscribed. + * @param eventFilter A bitmask of the event types for which this listener is subscribed. * * @see #registerDisplayListener(DisplayListener, Handler) * @see #unregisterDisplayListener * */ @FlaggedApi(FLAG_DISPLAY_LISTENER_PERFORMANCE_IMPROVEMENTS) - public void registerDisplayListener(@NonNull Executor executor, @EventFlag long eventFlags, + public void registerDisplayListener(@NonNull Executor executor, @EventType long eventFilter, @NonNull DisplayListener listener) { mGlobal.registerDisplayListener(listener, executor, - mGlobal.mapFlagsToInternalEventFlag(eventFlags, 0), + mGlobal.mapFiltersToInternalEventFlag(eventFilter, 0), ActivityThread.currentPackageName()); } @@ -849,8 +858,8 @@ public final class DisplayManager { * @param listener The listener to register. * @param handler The handler on which the listener should be invoked, or null * if the listener should be invoked on the calling thread's looper. - * @param eventFlags A bitmask of the event types for which this listener is subscribed. - * @param privateEventFlags A bitmask of the private event types for which this listener + * @param eventFilter A bitmask of the event types for which this listener is subscribed. + * @param privateEventFilter A bitmask of the private event types for which this listener * is subscribed. * * @see #registerDisplayListener(DisplayListener, Handler) @@ -859,10 +868,10 @@ public final class DisplayManager { * @hide */ public void registerDisplayListener(@NonNull DisplayListener listener, - @Nullable Handler handler, @EventFlag long eventFlags, - @PrivateEventFlag long privateEventFlags) { + @Nullable Handler handler, @EventType long eventFilter, + @PrivateEventType long privateEventFilter) { mGlobal.registerDisplayListener(listener, handler, - mGlobal.mapFlagsToInternalEventFlag(eventFlags, privateEventFlags), + mGlobal.mapFiltersToInternalEventFlag(eventFilter, privateEventFilter), ActivityThread.currentPackageName()); } diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java index 9030810a1c1a..b5715ed25bd9 100644 --- a/core/java/android/hardware/display/DisplayManagerGlobal.java +++ b/core/java/android/hardware/display/DisplayManagerGlobal.java @@ -18,7 +18,7 @@ package android.hardware.display; import static android.app.PropertyInvalidatedCache.MODULE_SYSTEM; -import static android.hardware.display.DisplayManager.EventFlag; +import static android.hardware.display.DisplayManager.EventType; import static android.Manifest.permission.MANAGE_DISPLAYS; import static android.view.Display.HdrCapabilities.HdrType; @@ -1737,35 +1737,35 @@ public final class DisplayManagerGlobal { * @return returns the bitmask of both public and private event flags unified to * InternalEventFlag */ - public @InternalEventFlag long mapFlagsToInternalEventFlag(@EventFlag long eventFlags, - @DisplayManager.PrivateEventFlag long privateEventFlags) { + public @InternalEventFlag long mapFiltersToInternalEventFlag(@EventType long eventFlags, + @DisplayManager.PrivateEventType long privateEventFlags) { return mapPrivateEventFlags(privateEventFlags) | mapPublicEventFlags(eventFlags); } - private long mapPrivateEventFlags(@DisplayManager.PrivateEventFlag long privateEventFlags) { + private long mapPrivateEventFlags(@DisplayManager.PrivateEventType long privateEventFlags) { long baseEventMask = 0; - if ((privateEventFlags & DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS) != 0) { + if ((privateEventFlags & DisplayManager.PRIVATE_EVENT_TYPE_DISPLAY_BRIGHTNESS) != 0) { baseEventMask |= INTERNAL_EVENT_FLAG_DISPLAY_BRIGHTNESS_CHANGED; } - if ((privateEventFlags & DisplayManager.PRIVATE_EVENT_FLAG_HDR_SDR_RATIO_CHANGED) != 0) { + if ((privateEventFlags & DisplayManager.PRIVATE_EVENT_TYPE_HDR_SDR_RATIO_CHANGED) != 0) { baseEventMask |= INTERNAL_EVENT_FLAG_DISPLAY_HDR_SDR_RATIO_CHANGED; } if ((privateEventFlags - & DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_CONNECTION_CHANGED) != 0) { + & DisplayManager.PRIVATE_EVENT_TYPE_DISPLAY_CONNECTION_CHANGED) != 0) { baseEventMask |= INTERNAL_EVENT_FLAG_DISPLAY_CONNECTION_CHANGED; } return baseEventMask; } - private long mapPublicEventFlags(@EventFlag long eventFlags) { + private long mapPublicEventFlags(@EventType long eventFlags) { long baseEventMask = 0; - if ((eventFlags & DisplayManager.EVENT_FLAG_DISPLAY_ADDED) != 0) { + if ((eventFlags & DisplayManager.EVENT_TYPE_DISPLAY_ADDED) != 0) { baseEventMask |= INTERNAL_EVENT_FLAG_DISPLAY_ADDED; } - if ((eventFlags & DisplayManager.EVENT_FLAG_DISPLAY_CHANGED) != 0) { + if ((eventFlags & DisplayManager.EVENT_TYPE_DISPLAY_CHANGED) != 0) { // For backward compatibility, a client subscribing to // DisplayManager.EVENT_FLAG_DISPLAY_CHANGED will be enrolled to both Basic and // RR changes @@ -1774,16 +1774,16 @@ public final class DisplayManagerGlobal { } if ((eventFlags - & DisplayManager.EVENT_FLAG_DISPLAY_REMOVED) != 0) { + & DisplayManager.EVENT_TYPE_DISPLAY_REMOVED) != 0) { baseEventMask |= INTERNAL_EVENT_FLAG_DISPLAY_REMOVED; } if (Flags.displayListenerPerformanceImprovements()) { - if ((eventFlags & DisplayManager.EVENT_FLAG_DISPLAY_REFRESH_RATE) != 0) { + if ((eventFlags & DisplayManager.EVENT_TYPE_DISPLAY_REFRESH_RATE) != 0) { baseEventMask |= INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE; } - if ((eventFlags & DisplayManager.EVENT_FLAG_DISPLAY_STATE) != 0) { + if ((eventFlags & DisplayManager.EVENT_TYPE_DISPLAY_STATE) != 0) { baseEventMask |= INTERNAL_EVENT_FLAG_DISPLAY_STATE; } } diff --git a/core/java/android/hardware/input/InputManagerGlobal.java b/core/java/android/hardware/input/InputManagerGlobal.java index 5c11346df1c3..3ef90e4b8a5f 100644 --- a/core/java/android/hardware/input/InputManagerGlobal.java +++ b/core/java/android/hardware/input/InputManagerGlobal.java @@ -63,6 +63,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.SomeArgs; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.concurrent.Executor; @@ -76,8 +77,9 @@ import java.util.concurrent.Executor; public final class InputManagerGlobal { private static final String TAG = "InputManagerGlobal"; // To enable these logs, run: 'adb shell setprop log.tag.InputManagerGlobal DEBUG' - // (requires restart) - private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + private boolean debug() { + return Log.isLoggable(TAG, Log.DEBUG); + } @GuardedBy("mInputDeviceListeners") @Nullable private SparseArray<InputDevice> mInputDevices; @@ -269,16 +271,19 @@ public final class InputManagerGlobal { } private void onInputDevicesChanged(int[] deviceIdAndGeneration) { - if (DEBUG) { - Log.d(TAG, "Received input devices changed."); + final boolean enableDebugLogs = debug(); + if (enableDebugLogs) { + Log.d(TAG, "Received input devices changed: " + Arrays.toString(deviceIdAndGeneration)); } synchronized (mInputDeviceListeners) { for (int i = mInputDevices.size(); --i > 0; ) { final int deviceId = mInputDevices.keyAt(i); if (!containsDeviceId(deviceIdAndGeneration, deviceId)) { - if (DEBUG) { - Log.d(TAG, "Device removed: " + deviceId); + if (enableDebugLogs) { + final InputDevice device = mInputDevices.valueAt(i); + final String name = device != null ? device.getName() : "<null>"; + Log.d(TAG, "Device removed: " + deviceId + " (" + name + ")"); } mInputDevices.removeAt(i); if (mInputDeviceSensorManager != null) { @@ -297,8 +302,9 @@ public final class InputManagerGlobal { if (device != null) { final int generation = deviceIdAndGeneration[i + 1]; if (device.getGeneration() != generation) { - if (DEBUG) { - Log.d(TAG, "Device changed: " + deviceId); + if (enableDebugLogs) { + Log.d(TAG, "Device changed: " + deviceId + " (" + + device.getName() + ")"); } mInputDevices.setValueAt(index, null); if (mInputDeviceSensorManager != null) { @@ -309,7 +315,7 @@ public final class InputManagerGlobal { } } } else { - if (DEBUG) { + if (enableDebugLogs) { Log.d(TAG, "Device added: " + deviceId); } mInputDevices.put(deviceId, null); @@ -517,7 +523,7 @@ public final class InputManagerGlobal { } private void onTabletModeChanged(long whenNanos, boolean inTabletMode) { - if (DEBUG) { + if (debug()) { Log.d(TAG, "Received tablet mode changed: " + "whenNanos=" + whenNanos + ", inTabletMode=" + inTabletMode); } diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index d54dbad9286c..c51ad9e986ef 100644 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -431,13 +431,8 @@ public class Build { * android.os.Build.VERSION_CODES_FULL}. */ @FlaggedApi(Flags.FLAG_MAJOR_MINOR_VERSIONING_SCHEME) - public static final int SDK_INT_FULL; - - static { - SDK_INT_FULL = VERSION_CODES_FULL.SDK_INT_MULTIPLIER - * SystemProperties.getInt("ro.build.version.sdk", 0) - + SystemProperties.getInt("ro.build.version.sdk_minor", 0); - } + public static final int SDK_INT_FULL = parseFullVersion(SystemProperties.get( + "ro.build.version.sdk_full", "")); /** * The SDK version of the software that <em>initially</em> shipped on diff --git a/core/java/android/os/IHintManager.aidl b/core/java/android/os/IHintManager.aidl index 56a089aff78a..5128e9173358 100644 --- a/core/java/android/os/IHintManager.aidl +++ b/core/java/android/os/IHintManager.aidl @@ -39,12 +39,18 @@ interface IHintManager { * Throws UnsupportedOperationException if ADPF is not supported, and IllegalStateException * if creation is supported but fails. */ - IHintSession createHintSessionWithConfig(in IBinder token, in SessionTag tag, + SessionCreationReturn createHintSessionWithConfig(in IBinder token, in SessionTag tag, in SessionCreationConfig creationConfig, out SessionConfig config); void setHintSessionThreads(in IHintSession hintSession, in int[] tids); int[] getHintSessionThreadIds(in IHintSession hintSession); + parcelable SessionCreationReturn { + IHintSession session; + // True if the graphics pipeline thread limit is being exceeded + boolean pipelineThreadLimitExceeded = false; + } + /** * Returns FMQ channel information for the caller, which it associates to a binder token. * diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 507bcb8c2717..08f68f1874e7 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -4206,12 +4206,16 @@ public class UserManager { private boolean getUserRestrictionFromQuery(@NonNull Pair<String, Integer> restrictionPerUser) { return UserManagerCache.getUserRestrictionFromQuery( (Pair<String, Integer> q) -> mService.hasUserRestriction(q.first, q.second), + // bypass cache if the flag is disabled + (Pair<String, Integer> q) -> !android.multiuser.Flags.cacheUserRestrictionsReadOnly(), restrictionPerUser); } /** @hide */ public static final void invalidateUserRestriction() { - UserManagerCache.invalidateUserRestrictionFromQuery(); + if (android.multiuser.Flags.cacheUserRestrictionsReadOnly()) { + UserManagerCache.invalidateUserRestrictionFromQuery(); + } } /** diff --git a/core/java/com/android/internal/display/BrightnessSynchronizer.java b/core/java/com/android/internal/display/BrightnessSynchronizer.java index a50dbb0223b7..50f10002f7bc 100644 --- a/core/java/com/android/internal/display/BrightnessSynchronizer.java +++ b/core/java/com/android/internal/display/BrightnessSynchronizer.java @@ -601,7 +601,7 @@ public class BrightnessSynchronizer { cr.registerContentObserver(BRIGHTNESS_URI, false, createBrightnessContentObserver(handler), UserHandle.USER_ALL); mDisplayManager.registerDisplayListener(mListener, handler, /* eventFlags */ 0, - DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS); + DisplayManager.PRIVATE_EVENT_TYPE_DISPLAY_BRIGHTNESS); mIsObserving = true; } } diff --git a/core/java/com/android/internal/widget/NotificationExpandButton.java b/core/java/com/android/internal/widget/NotificationExpandButton.java index d4dd1e705653..751cfde70164 100644 --- a/core/java/com/android/internal/widget/NotificationExpandButton.java +++ b/core/java/com/android/internal/widget/NotificationExpandButton.java @@ -16,6 +16,8 @@ package com.android.internal.widget; +import static android.app.Flags.notificationsRedesignTemplates; + import android.annotation.ColorInt; import android.annotation.Nullable; import android.content.Context; @@ -130,10 +132,18 @@ public class NotificationExpandButton extends FrameLayout { int drawableId; int contentDescriptionId; if (mExpanded) { - drawableId = R.drawable.ic_collapse_notification; + if (notificationsRedesignTemplates()) { + drawableId = R.drawable.ic_notification_2025_collapse; + } else { + drawableId = R.drawable.ic_collapse_notification; + } contentDescriptionId = R.string.expand_button_content_description_expanded; } else { - drawableId = R.drawable.ic_expand_notification; + if (notificationsRedesignTemplates()) { + drawableId = R.drawable.ic_notification_2025_expand; + } else { + drawableId = R.drawable.ic_expand_notification; + } contentDescriptionId = R.string.expand_button_content_description_collapsed; } setContentDescription(mContext.getText(contentDescriptionId)); diff --git a/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java b/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java index fb560a568a9c..26b0d11955d2 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java +++ b/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java @@ -62,7 +62,7 @@ public class CoreDocument { // We also keep a more fine-grained BUILD number, exposed as // ID_API_LEVEL = DOCUMENT_API_LEVEL + BUILD - static final float BUILD = 0.1f; + static final float BUILD = 0.2f; @NonNull ArrayList<Operation> mOperations = new ArrayList<>(); @@ -1058,7 +1058,7 @@ public class CoreDocument { * @param theme the theme we want to use for this document. */ public void paint(@NonNull RemoteContext context, int theme) { - context.getLastOpCount(); + context.clearLastOpCount(); context.getPaintContext().clearNeedsRepaint(); context.loadFloat(RemoteContext.ID_DENSITY, context.getDensity()); context.mMode = RemoteContext.ContextMode.UNSET; diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java index cd26198caf2e..43f8ea7dc78f 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java +++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java @@ -528,6 +528,7 @@ public class RemoteComposeState implements CollectionsAccess { public void setContext(@NonNull RemoteContext context) { mRemoteContext = context; + mRemoteContext.clearLastOpCount(); } public void updateObject(int id, @NonNull Object value) { diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java index ec336633e960..23c362830713 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java +++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java @@ -743,4 +743,9 @@ public abstract class RemoteContext { mOpCount = 0; return count; } + + /** Explicitly clear the operation counter */ + public void clearLastOpCount() { + mOpCount = 0; + } } diff --git a/core/res/res/drawable/ic_notification_2025_collapse.xml b/core/res/res/drawable/ic_notification_2025_collapse.xml new file mode 100644 index 000000000000..1b40c555f84d --- /dev/null +++ b/core/res/res/drawable/ic_notification_2025_collapse.xml @@ -0,0 +1,25 @@ +<!-- + ~ Copyright (C) 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. + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="960" + android:viewportHeight="960" + android:tint="?attr/colorControlNormal"> + <path + android:fillColor="@android:color/white" + android:pathData="M480,432L296,616L240,560L480,320L720,560L664,616L480,432Z"/> +</vector> diff --git a/core/res/res/drawable/ic_notification_2025_expand.xml b/core/res/res/drawable/ic_notification_2025_expand.xml new file mode 100644 index 000000000000..ea5e0f09d2ef --- /dev/null +++ b/core/res/res/drawable/ic_notification_2025_expand.xml @@ -0,0 +1,25 @@ +<!-- + ~ Copyright (C) 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. + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="960" + android:viewportHeight="960" + android:tint="?attr/colorControlNormal"> + <path + android:fillColor="@android:color/white" + android:pathData="M480,616L240,376L296,320L480,504L664,320L720,376L480,616Z"/> +</vector> diff --git a/core/res/res/drawable/notification_2025_expand_button_pill_bg.xml b/core/res/res/drawable/notification_2025_expand_button_pill_bg.xml new file mode 100644 index 000000000000..74f697a176f3 --- /dev/null +++ b/core/res/res/drawable/notification_2025_expand_button_pill_bg.xml @@ -0,0 +1,29 @@ +<!-- + ~ Copyright (C) 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. + --> +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@+id/expand_button_pill_colorized_layer"> + <shape xmlns:android="http://schemas.android.com/apk/res/android"> + <corners android:radius="@dimen/notification_2025_expand_button_pill_height" /> + <solid android:color="@android:color/white" /> + </shape> + </item> + <item> + <shape xmlns:android="http://schemas.android.com/apk/res/android"> + <corners android:radius="@dimen/notification_2025_expand_button_pill_height" /> + <solid android:color="@color/notification_expand_button_state_tint" /> + </shape> + </item> +</layer-list> diff --git a/core/res/res/layout/notification_2025_expand_button.xml b/core/res/res/layout/notification_2025_expand_button.xml new file mode 100644 index 000000000000..c8263c26f38f --- /dev/null +++ b/core/res/res/layout/notification_2025_expand_button.xml @@ -0,0 +1,61 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (C) 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 + --> + +<!-- extends FrameLayout --> +<com.android.internal.widget.NotificationExpandButton + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/expand_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="top|end" + android:contentDescription="@string/expand_button_content_description_collapsed" + > + + <LinearLayout + android:id="@+id/expand_button_pill" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:minHeight="@dimen/notification_2025_expand_button_pill_height" + android:minWidth="@dimen/notification_2025_expand_button_pill_width" + android:paddingVertical="@dimen/notification_2025_expand_button_vertical_icon_padding" + android:paddingHorizontal="@dimen/notification_2025_expand_button_horizontal_icon_padding" + android:orientation="horizontal" + android:background="@drawable/notification_2025_expand_button_pill_bg" + android:gravity="center" + android:layout_gravity="center_vertical" + android:duplicateParentState="true" + > + + <TextView + android:id="@+id/expand_button_number" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info" + android:gravity="center_vertical" + android:visibility="gone" + /> + + <ImageView + android:id="@+id/expand_button_icon" + android:layout_width="@dimen/notification_2025_expand_button_icon_size" + android:layout_height="@dimen/notification_2025_expand_button_icon_size" + android:scaleType="fitCenter" + android:importantForAccessibility="no" + /> + + </LinearLayout> + +</com.android.internal.widget.NotificationExpandButton> diff --git a/core/res/res/layout/notification_2025_template_collapsed_base.xml b/core/res/res/layout/notification_2025_template_collapsed_base.xml index e91e1115ac1c..c827dcb16584 100644 --- a/core/res/res/layout/notification_2025_template_collapsed_base.xml +++ b/core/res/res/layout/notification_2025_template_collapsed_base.xml @@ -164,10 +164,11 @@ android:minWidth="@dimen/notification_content_margin_end" > - <include layout="@layout/notification_expand_button" + <include layout="@layout/notification_2025_expand_button" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_gravity="center_vertical|end" + android:layout_gravity="top|end" + android:layout_margin="@dimen/notification_2025_margin" /> </FrameLayout> diff --git a/core/res/res/layout/notification_2025_template_collapsed_call.xml b/core/res/res/layout/notification_2025_template_collapsed_call.xml index c4bca1142ece..ce38c1645fb1 100644 --- a/core/res/res/layout/notification_2025_template_collapsed_call.xml +++ b/core/res/res/layout/notification_2025_template_collapsed_call.xml @@ -66,10 +66,11 @@ > <include - layout="@layout/notification_expand_button" + layout="@layout/notification_2025_expand_button" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_gravity="center_vertical" + android:layout_gravity="top|end" + android:layout_margin="@dimen/notification_2025_margin" /> </FrameLayout> diff --git a/core/res/res/layout/notification_2025_template_collapsed_media.xml b/core/res/res/layout/notification_2025_template_collapsed_media.xml index 2d367337bb6f..0021b8384847 100644 --- a/core/res/res/layout/notification_2025_template_collapsed_media.xml +++ b/core/res/res/layout/notification_2025_template_collapsed_media.xml @@ -185,10 +185,11 @@ android:minWidth="@dimen/notification_content_margin_end" > - <include layout="@layout/notification_expand_button" + <include layout="@layout/notification_2025_expand_button" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_gravity="center_vertical|end" + android:layout_gravity="top|end" + android:layout_margin="@dimen/notification_2025_margin" /> </FrameLayout> diff --git a/core/res/res/layout/notification_2025_template_collapsed_messaging.xml b/core/res/res/layout/notification_2025_template_collapsed_messaging.xml index fbecb8c30b9c..f3e4ce13ff4b 100644 --- a/core/res/res/layout/notification_2025_template_collapsed_messaging.xml +++ b/core/res/res/layout/notification_2025_template_collapsed_messaging.xml @@ -189,10 +189,11 @@ android:minWidth="@dimen/notification_content_margin_end" > - <include layout="@layout/notification_expand_button" + <include layout="@layout/notification_2025_expand_button" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_gravity="center_vertical|end" + android:layout_gravity="top|end" + android:layout_margin="@dimen/notification_2025_margin" /> </FrameLayout> diff --git a/core/res/res/layout/notification_2025_template_conversation.xml b/core/res/res/layout/notification_2025_template_conversation.xml index f31f65e90950..6be5a1cee7f9 100644 --- a/core/res/res/layout/notification_2025_template_conversation.xml +++ b/core/res/res/layout/notification_2025_template_conversation.xml @@ -148,10 +148,11 @@ android:clipToPadding="false" android:clipChildren="false" /> - <include layout="@layout/notification_expand_button" + <include layout="@layout/notification_2025_expand_button" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_gravity="center" + android:layout_gravity="top|end" + android:layout_margin="@dimen/notification_2025_margin" /> </LinearLayout> </LinearLayout> diff --git a/core/res/res/layout/notification_2025_template_expanded_base.xml b/core/res/res/layout/notification_2025_template_expanded_base.xml index e480fe5eef1e..d364c659d0db 100644 --- a/core/res/res/layout/notification_2025_template_expanded_base.xml +++ b/core/res/res/layout/notification_2025_template_expanded_base.xml @@ -40,13 +40,14 @@ <include layout="@layout/notification_2025_template_header" /> + <!-- Note: the top margin is being set in code based on the estimated space needed for + the header text. --> <LinearLayout android:id="@+id/notification_main_column" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginStart="@dimen/notification_2025_content_margin_start" android:layout_marginEnd="@dimen/notification_content_margin_end" - android:layout_marginTop="@dimen/notification_content_margin_top" android:orientation="vertical" > diff --git a/core/res/res/layout/notification_2025_template_expanded_big_picture.xml b/core/res/res/layout/notification_2025_template_expanded_big_picture.xml index 18bafe068fcb..12e11728f608 100644 --- a/core/res/res/layout/notification_2025_template_expanded_big_picture.xml +++ b/core/res/res/layout/notification_2025_template_expanded_big_picture.xml @@ -32,11 +32,12 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="top" - android:layout_marginTop="@dimen/notification_content_margin_top" android:clipToPadding="false" android:orientation="vertical" > + <!-- Note: the top margin is being set in code based on the estimated space needed for + the header text. --> <LinearLayout android:id="@+id/notification_main_column" android:layout_width="match_parent" diff --git a/core/res/res/layout/notification_2025_template_expanded_big_text.xml b/core/res/res/layout/notification_2025_template_expanded_big_text.xml index 9ff141b7c946..c9dd868795de 100644 --- a/core/res/res/layout/notification_2025_template_expanded_big_text.xml +++ b/core/res/res/layout/notification_2025_template_expanded_big_text.xml @@ -30,12 +30,13 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="top" - android:layout_marginTop="@dimen/notification_content_margin_top" android:layout_marginBottom="@dimen/notification_content_margin" android:clipToPadding="false" android:orientation="vertical" > + <!-- Note: the top margin is being set in code based on the estimated space needed for + the header text. --> <com.android.internal.widget.RemeasuringLinearLayout android:id="@+id/notification_main_column" android:layout_width="match_parent" diff --git a/core/res/res/layout/notification_2025_template_expanded_call.xml b/core/res/res/layout/notification_2025_template_expanded_call.xml index 2af0ec2972df..0be61253c917 100644 --- a/core/res/res/layout/notification_2025_template_expanded_call.xml +++ b/core/res/res/layout/notification_2025_template_expanded_call.xml @@ -48,8 +48,8 @@ android:id="@+id/notification_main_column" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_weight="1" android:layout_marginStart="@dimen/notification_2025_content_margin_start" + android:layout_weight="1" android:orientation="vertical" android:minHeight="68dp" > diff --git a/core/res/res/layout/notification_2025_template_expanded_inbox.xml b/core/res/res/layout/notification_2025_template_expanded_inbox.xml index 9fb44ccccfa0..8434b3644f81 100644 --- a/core/res/res/layout/notification_2025_template_expanded_inbox.xml +++ b/core/res/res/layout/notification_2025_template_expanded_inbox.xml @@ -28,10 +28,11 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="top" - android:layout_marginTop="@dimen/notification_content_margin_top" android:clipToPadding="false" android:orientation="vertical"> + <!-- Note: the top margin is being set in code based on the estimated space needed for + the header text. --> <LinearLayout android:id="@+id/notification_main_column" android:layout_width="match_parent" diff --git a/core/res/res/layout/notification_2025_template_expanded_media.xml b/core/res/res/layout/notification_2025_template_expanded_media.xml index 578a0b2b6d0b..e90ab792581f 100644 --- a/core/res/res/layout/notification_2025_template_expanded_media.xml +++ b/core/res/res/layout/notification_2025_template_expanded_media.xml @@ -34,11 +34,12 @@ android:id="@+id/notification_media_content" > + <!-- Note: the top margin is being set in code based on the estimated space needed for + the header text. --> <LinearLayout android:id="@+id/notification_main_column" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginTop="@dimen/notification_content_margin_top" android:layout_marginStart="@dimen/notification_2025_content_margin_start" android:layout_marginEnd="@dimen/notification_content_margin_end" android:orientation="vertical" diff --git a/core/res/res/layout/notification_2025_template_expanded_messaging.xml b/core/res/res/layout/notification_2025_template_expanded_messaging.xml index e3c201465eb0..7f5a36b5f865 100644 --- a/core/res/res/layout/notification_2025_template_expanded_messaging.xml +++ b/core/res/res/layout/notification_2025_template_expanded_messaging.xml @@ -33,10 +33,11 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="top" - android:layout_marginTop="@dimen/notification_2025_header_height" android:clipChildren="false" android:orientation="vertical"> + <!-- Note: the top margin is being set in code based on the estimated space needed for + the header text. --> <com.android.internal.widget.RemeasuringLinearLayout android:id="@+id/notification_main_column" android:layout_width="match_parent" diff --git a/core/res/res/layout/notification_2025_template_expanded_progress.xml b/core/res/res/layout/notification_2025_template_expanded_progress.xml index afa4bc6dd7f8..5d4fc4c87fac 100644 --- a/core/res/res/layout/notification_2025_template_expanded_progress.xml +++ b/core/res/res/layout/notification_2025_template_expanded_progress.xml @@ -41,13 +41,14 @@ <include layout="@layout/notification_2025_template_header" /> + <!-- Note: the top margin is being set in code based on the estimated space needed for + the header text. --> <LinearLayout android:id="@+id/notification_main_column" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginStart="@dimen/notification_2025_content_margin_start" android:layout_marginEnd="@dimen/notification_content_margin_end" - android:layout_marginTop="@dimen/notification_content_margin_top" android:orientation="vertical" > diff --git a/core/res/res/layout/notification_2025_template_header.xml b/core/res/res/layout/notification_2025_template_header.xml index 2d30d8a8bbb6..3f34eb3cbf0c 100644 --- a/core/res/res/layout/notification_2025_template_header.xml +++ b/core/res/res/layout/notification_2025_template_header.xml @@ -57,11 +57,11 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/notification_top_line" android:layout_width="wrap_content" - android:layout_height="match_parent" + android:layout_height="wrap_content" android:layout_alignParentStart="true" - android:layout_centerVertical="true" android:layout_toStartOf="@id/expand_button" android:layout_alignWithParentIfMissing="true" + android:layout_marginVertical="@dimen/notification_2025_margin" android:clipChildren="false" android:gravity="center_vertical" android:paddingStart="@dimen/notification_2025_content_margin_start" @@ -81,10 +81,11 @@ android:focusable="false" /> - <include layout="@layout/notification_expand_button" + <include layout="@layout/notification_2025_expand_button" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_centerVertical="true" + android:layout_gravity="top|end" + android:layout_margin="@dimen/notification_2025_margin" android:layout_alignParentEnd="true" /> <include layout="@layout/notification_close_button" diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index fb21c7532ea3..a4735fe6c7af 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -310,9 +310,15 @@ <!-- Size of the stroke with for the emphasized notification button style --> <dimen name="emphasized_button_stroke_width">1dp</dimen> - <!-- height of the content margin to accomodate for the header --> + <!-- height of the content margin to accommodate for the header --> <dimen name="notification_content_margin_top">50dp</dimen> + <!-- The spacing between the content and the header text above it, scaling with text size. + This value is chosen so that, taking into account the text spacing for both the text in the + top line and the text in the content, the distance between them is 4dp with the default + screen configuration (and will grow accordingly for larger font sizes) --> + <dimen name="notification_2025_content_margin_top">10sp</dimen> + <!-- height of the content margin that is applied at the end of the notification content --> <dimen name="notification_content_margin">20dp</dimen> @@ -384,9 +390,24 @@ <!-- the height of the expand button pill --> <dimen name="notification_expand_button_pill_height">24dp</dimen> + <!-- the height of the expand button pill (2025 redesign version) --> + <dimen name="notification_2025_expand_button_pill_height">20dp</dimen> + + <!-- the width of the expand button pill (2025 redesign version) --> + <dimen name="notification_2025_expand_button_pill_width">28dp</dimen> + + <!-- the size of the expand arrow (2025 redesign version) --> + <dimen name="notification_2025_expand_button_icon_size">16sp</dimen> + <!-- the padding of the expand icon in the notification header --> <dimen name="notification_expand_button_icon_padding">2dp</dimen> + <!-- the padding of the expand icon in the notification header --> + <dimen name="notification_2025_expand_button_vertical_icon_padding">2dp</dimen> + + <!-- the padding of the expand icon in the notification header --> + <dimen name="notification_2025_expand_button_horizontal_icon_padding">6dp</dimen> + <!-- the size of the notification close button --> <dimen name="notification_close_button_size">16dp</dimen> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index cfc3ddca27eb..33d3858562af 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -246,6 +246,8 @@ <string name="NetworkPreferenceSwitchSummary">Try changing preferred network. Tap to change.</string> <!-- Displayed to tell the user that emergency calls might not be available. --> <string name="EmergencyCallWarningTitle">Emergency calling unavailable</string> + <!-- Displayed to tell the user that the notification can be permanently turned off --> + <string name="emergency_calling_do_not_show_again">Do Not Show Again</string> <!-- Displayed to tell the user that emergency calls might not be available; this is shown to the user when only WiFi calling is available and the carrier does not support emergency calls over WiFi calling. --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 8195d38993c8..6ee2839788af 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -640,6 +640,7 @@ <java-symbol type="string" name="auto_data_switch_title" /> <java-symbol type="string" name="auto_data_switch_content" /> <java-symbol type="string" name="RestrictedStateContentMsimTemplate" /> + <java-symbol type="string" name="emergency_calling_do_not_show_again" /> <java-symbol type="string" name="notification_channel_network_alert" /> <java-symbol type="string" name="notification_channel_call_forward" /> <java-symbol type="string" name="notification_channel_emergency_callback" /> @@ -3229,6 +3230,8 @@ <java-symbol type="id" name="header_text_divider" /> <java-symbol type="id" name="header_text_secondary_divider" /> <java-symbol type="drawable" name="ic_expand_notification" /> + <java-symbol type="drawable" name="ic_notification_2025_collapse" /> + <java-symbol type="drawable" name="ic_notification_2025_expand" /> <java-symbol type="drawable" name="ic_collapse_notification" /> <java-symbol type="drawable" name="notification_close_button_icon" /> <java-symbol type="drawable" name="ic_expand_bundle" /> @@ -3241,6 +3244,8 @@ <java-symbol type="dimen" name="notification_heading_margin_end" /> <java-symbol type="dimen" name="notification_content_margin_top" /> <java-symbol type="dimen" name="notification_content_margin" /> + <java-symbol type="dimen" name="notification_2025_margin" /> + <java-symbol type="dimen" name="notification_2025_content_margin_top" /> <java-symbol type="dimen" name="notification_progress_margin_horizontal" /> <java-symbol type="dimen" name="notification_header_background_height" /> <java-symbol type="dimen" name="notification_header_touchable_height" /> diff --git a/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java b/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java index a270848b98a3..8fa510381060 100644 --- a/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java +++ b/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java @@ -302,48 +302,48 @@ public class DisplayManagerGlobalTest { @Test @RequiresFlagsEnabled(Flags.FLAG_DISPLAY_LISTENER_PERFORMANCE_IMPROVEMENTS) - public void testMapFlagsToInternalEventFlag() { + public void testMapFiltersToInternalEventFlag() { // Test public flags mapping assertEquals(DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_ADDED, mDisplayManagerGlobal - .mapFlagsToInternalEventFlag(DisplayManager.EVENT_FLAG_DISPLAY_ADDED, 0)); + .mapFiltersToInternalEventFlag(DisplayManager.EVENT_TYPE_DISPLAY_ADDED, 0)); assertEquals(DISPLAY_CHANGE_EVENTS, mDisplayManagerGlobal - .mapFlagsToInternalEventFlag(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED, 0)); + .mapFiltersToInternalEventFlag(DisplayManager.EVENT_TYPE_DISPLAY_CHANGED, 0)); assertEquals(DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REMOVED, - mDisplayManagerGlobal - .mapFlagsToInternalEventFlag(DisplayManager.EVENT_FLAG_DISPLAY_REMOVED, 0)); + mDisplayManagerGlobal.mapFiltersToInternalEventFlag( + DisplayManager.EVENT_TYPE_DISPLAY_REMOVED, 0)); assertEquals(INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE, mDisplayManagerGlobal - .mapFlagsToInternalEventFlag( - DisplayManager.EVENT_FLAG_DISPLAY_REFRESH_RATE, + .mapFiltersToInternalEventFlag( + DisplayManager.EVENT_TYPE_DISPLAY_REFRESH_RATE, 0)); assertEquals(DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_STATE, mDisplayManagerGlobal - .mapFlagsToInternalEventFlag( - DisplayManager.EVENT_FLAG_DISPLAY_STATE, + .mapFiltersToInternalEventFlag( + DisplayManager.EVENT_TYPE_DISPLAY_STATE, 0)); // test private flags mapping assertEquals(DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_CONNECTION_CHANGED, mDisplayManagerGlobal - .mapFlagsToInternalEventFlag(0, - DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_CONNECTION_CHANGED)); + .mapFiltersToInternalEventFlag(0, + DisplayManager.PRIVATE_EVENT_TYPE_DISPLAY_CONNECTION_CHANGED)); assertEquals(DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_HDR_SDR_RATIO_CHANGED, mDisplayManagerGlobal - .mapFlagsToInternalEventFlag(0, - DisplayManager.PRIVATE_EVENT_FLAG_HDR_SDR_RATIO_CHANGED)); + .mapFiltersToInternalEventFlag(0, + DisplayManager.PRIVATE_EVENT_TYPE_HDR_SDR_RATIO_CHANGED)); assertEquals(DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_BRIGHTNESS_CHANGED, mDisplayManagerGlobal - .mapFlagsToInternalEventFlag(0, - DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS)); + .mapFiltersToInternalEventFlag(0, + DisplayManager.PRIVATE_EVENT_TYPE_DISPLAY_BRIGHTNESS)); // Test both public and private flags mapping assertEquals(DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_BRIGHTNESS_CHANGED | INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE, mDisplayManagerGlobal - .mapFlagsToInternalEventFlag( - DisplayManager.EVENT_FLAG_DISPLAY_REFRESH_RATE, - DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS)); + .mapFiltersToInternalEventFlag( + DisplayManager.EVENT_TYPE_DISPLAY_REFRESH_RATE, + DisplayManager.PRIVATE_EVENT_TYPE_DISPLAY_BRIGHTNESS)); } private void waitForHandler() { diff --git a/graphics/java/android/graphics/BlendModeColorFilter.java b/graphics/java/android/graphics/BlendModeColorFilter.java index 7caeb4267ad2..d4e23732bdc3 100644 --- a/graphics/java/android/graphics/BlendModeColorFilter.java +++ b/graphics/java/android/graphics/BlendModeColorFilter.java @@ -71,7 +71,7 @@ public final class BlendModeColorFilter extends ColorFilter { return false; } final BlendModeColorFilter other = (BlendModeColorFilter) object; - return other.mMode == mMode; + return (other.mMode == mMode && other.mColor == mColor); } @Override diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/RearDisplayPresentationRequestCallback.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/RearDisplayPresentationRequestCallback.java index ea33426d0bdf..9f978e000493 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/RearDisplayPresentationRequestCallback.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/RearDisplayPresentationRequestCallback.java @@ -43,10 +43,7 @@ public class RearDisplayPresentationRequestCallback implements DeviceStateReques public RearDisplayPresentationRequestCallback(@NonNull Context context, @NonNull RearDisplayPresentationController rearDisplayPresentationController) { mDisplayManager = context.getSystemService(DisplayManager.class); - mDisplayManager.registerDisplayListener(mRearDisplayListener, - context.getMainThreadHandler(), DisplayManager.EVENT_FLAG_DISPLAY_ADDED - | DisplayManager.EVENT_FLAG_DISPLAY_CHANGED); - + registerDisplayListener(context); mRearDisplayPresentationController = rearDisplayPresentationController; } @@ -112,4 +109,10 @@ public class RearDisplayPresentationRequestCallback implements DeviceStateReques mRearDisplayPresentationController.startSession(rearDisplay); } } + + private void registerDisplayListener(Context context) { + mDisplayManager.registerDisplayListener(mRearDisplayListener, + context.getMainThreadHandler(), DisplayManager.EVENT_TYPE_DISPLAY_ADDED + | DisplayManager.EVENT_TYPE_DISPLAY_CHANGED); + } } diff --git a/libs/WindowManager/Shell/multivalentTests/AndroidManifest.xml b/libs/WindowManager/Shell/multivalentTests/AndroidManifest.xml index f8f8338e5f04..fd578a959e3b 100644 --- a/libs/WindowManager/Shell/multivalentTests/AndroidManifest.xml +++ b/libs/WindowManager/Shell/multivalentTests/AndroidManifest.xml @@ -3,6 +3,8 @@ <application android:debuggable="true" android:supportsRtl="true" > <uses-library android:name="android.test.runner" /> + <activity android:name="com.android.wm.shell.bubbles.bar.BubbleBarAnimationHelperTest$TestActivity" + android:exported="true"/> </application> <instrumentation diff --git a/libs/WindowManager/Shell/multivalentTests/AndroidManifestRobolectric.xml b/libs/WindowManager/Shell/multivalentTests/AndroidManifestRobolectric.xml index ffcd7d46fbae..bb111dbeffff 100644 --- a/libs/WindowManager/Shell/multivalentTests/AndroidManifestRobolectric.xml +++ b/libs/WindowManager/Shell/multivalentTests/AndroidManifestRobolectric.xml @@ -1,3 +1,8 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.wm.shell.multivalenttests"> + <application> + <activity android:name="com.android.wm.shell.bubbles.bar.BubbleBarAnimationHelperTest$TestActivity" + android:exported="true"/> + </application> </manifest> + diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleControllerBubbleBarTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleControllerBubbleBarTest.kt index 2b4e5417f188..c62d2a06bad5 100644 --- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleControllerBubbleBarTest.kt +++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleControllerBubbleBarTest.kt @@ -35,11 +35,11 @@ import com.android.internal.statusbar.IStatusBarService import com.android.wm.shell.Flags import com.android.wm.shell.ShellTaskOrganizer import com.android.wm.shell.TestShellExecutor -import com.android.wm.shell.WindowManagerShellWrapper import com.android.wm.shell.bubbles.Bubbles.SysuiProxy import com.android.wm.shell.bubbles.properties.ProdBubbleProperties import com.android.wm.shell.bubbles.storage.BubblePersistentRepository import com.android.wm.shell.common.DisplayController +import com.android.wm.shell.common.DisplayImeController import com.android.wm.shell.common.DisplayInsetsController import com.android.wm.shell.common.FloatingContentCoordinator import com.android.wm.shell.common.SyncTransactionQueue @@ -268,7 +268,8 @@ class BubbleControllerBubbleBarTest { bubbleDataRepository, mock<IStatusBarService>(), mock<WindowManager>(), - WindowManagerShellWrapper(mainExecutor), + mock<DisplayInsetsController>(), + mock<DisplayImeController>(), mock<UserManager>(), mock<LauncherApps>(), bubbleLogger, diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt index 680d015dfd2f..3043e2bcb0be 100644 --- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt +++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt @@ -36,10 +36,10 @@ import com.android.internal.statusbar.IStatusBarService import com.android.launcher3.icons.BubbleIconFactory import com.android.wm.shell.ShellTaskOrganizer import com.android.wm.shell.TestShellExecutor -import com.android.wm.shell.WindowManagerShellWrapper import com.android.wm.shell.bubbles.properties.BubbleProperties import com.android.wm.shell.bubbles.storage.BubblePersistentRepository import com.android.wm.shell.common.DisplayController +import com.android.wm.shell.common.DisplayImeController import com.android.wm.shell.common.DisplayInsetsController import com.android.wm.shell.common.FloatingContentCoordinator import com.android.wm.shell.common.SyncTransactionQueue @@ -141,7 +141,8 @@ class BubbleViewInfoTaskTest { bubbleDataRepository, mock<IStatusBarService>(), windowManager, - WindowManagerShellWrapper(mainExecutor), + mock<DisplayInsetsController>(), + mock<DisplayImeController>(), mock<UserManager>(), mock<LauncherApps>(), bubbleLogger, diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelperTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelperTest.kt index 3e01256fd67c..957f0ca502a1 100644 --- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelperTest.kt +++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelperTest.kt @@ -17,14 +17,19 @@ package com.android.wm.shell.bubbles.bar import android.animation.AnimatorTestRule +import android.app.Activity import android.app.ActivityManager import android.content.Context import android.graphics.Insets +import android.graphics.Outline import android.graphics.Rect +import android.os.Bundle import android.view.View import android.view.ViewGroup import android.view.WindowManager import android.widget.FrameLayout +import android.widget.FrameLayout.LayoutParams +import androidx.test.core.app.ActivityScenario import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest @@ -63,6 +68,7 @@ import org.mockito.kotlin.whenever class BubbleBarAnimationHelperTest { @get:Rule val animatorTestRule: AnimatorTestRule = AnimatorTestRule(this) + private lateinit var activityScenario: ActivityScenario<TestActivity> companion object { const val SCREEN_WIDTH = 2000 @@ -83,6 +89,8 @@ class BubbleBarAnimationHelperTest { fun setUp() { ProtoLog.REQUIRE_PROTOLOGTOOL = false ProtoLog.init() + activityScenario = ActivityScenario.launch(TestActivity::class.java) + activityScenario.onActivity { activity -> container = activity.container } val windowManager = context.getSystemService(WindowManager::class.java) bubblePositioner = BubblePositioner(context, windowManager) bubblePositioner.setShowingInBubbleBar(true) @@ -102,8 +110,6 @@ class BubbleBarAnimationHelperTest { mainExecutor = TestShellExecutor() bgExecutor = TestShellExecutor() - container = FrameLayout(context) - animationHelper = BubbleBarAnimationHelper(context, bubblePositioner) } @@ -121,7 +127,7 @@ class BubbleBarAnimationHelperTest { val semaphore = Semaphore(0) val after = Runnable { semaphore.release() } - getInstrumentation().runOnMainSync { + activityScenario.onActivity { animationHelper.animateSwitch(fromBubble, toBubble, after) animatorTestRule.advanceTimeBy(1000) } @@ -145,7 +151,7 @@ class BubbleBarAnimationHelperTest { .updateHandleColor(/* isRegionDark= */ true, /* animated= */ false) val toBubble = createBubble(key = "to").initialize(container) - getInstrumentation().runOnMainSync { + activityScenario.onActivity { animationHelper.animateSwitch(fromBubble, toBubble, /* afterAnimation= */ null) animatorTestRule.advanceTimeBy(1000) } @@ -161,7 +167,7 @@ class BubbleBarAnimationHelperTest { val toBubbleTaskController = mock<TaskViewTaskController>() val toBubble = createBubble("to", toBubbleTaskController).initialize(container) - getInstrumentation().runOnMainSync { + activityScenario.onActivity { animationHelper.animateSwitch(fromBubble, toBubble) {} // Start the animation, but don't finish animatorTestRule.advanceTimeBy(100) @@ -183,7 +189,7 @@ class BubbleBarAnimationHelperTest { val semaphore = Semaphore(0) val after = Runnable { semaphore.release() } - getInstrumentation().runOnMainSync { + activityScenario.onActivity { animationHelper.animateSwitch(fromBubble, overflow, after) animatorTestRule.advanceTimeBy(1000) } @@ -206,7 +212,7 @@ class BubbleBarAnimationHelperTest { val semaphore = Semaphore(0) val after = Runnable { semaphore.release() } - getInstrumentation().runOnMainSync { + activityScenario.onActivity { animationHelper.animateSwitch(overflow, toBubble, after) animatorTestRule.advanceTimeBy(1000) } @@ -226,7 +232,7 @@ class BubbleBarAnimationHelperTest { val taskController = mock<TaskViewTaskController>() val bubble = createBubble("key", taskController).initialize(container) - getInstrumentation().runOnMainSync { + activityScenario.onActivity { animationHelper.animateExpansion(bubble) {} animatorTestRule.advanceTimeBy(1000) } @@ -243,6 +249,80 @@ class BubbleBarAnimationHelperTest { verify(taskController).setWindowBounds(any()) } + @Test + fun animateExpansion() { + val bubble = createBubble(key = "b1").initialize(container) + val bbev = bubble.bubbleBarExpandedView!! + + val semaphore = Semaphore(0) + val after = Runnable { semaphore.release() } + + activityScenario.onActivity { + bbev.onTaskCreated() + animationHelper.animateExpansion(bubble, after) + animatorTestRule.advanceTimeBy(1000) + } + getInstrumentation().waitForIdleSync() + + assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue() + assertThat(bbev.alpha).isEqualTo(1) + } + + @Test + fun onImeTopChanged_noOverlap() { + val bubble = createBubble(key = "b1").initialize(container) + val bbev = bubble.bubbleBarExpandedView!! + + val semaphore = Semaphore(0) + val after = Runnable { semaphore.release() } + + activityScenario.onActivity { + bbev.onTaskCreated() + animationHelper.animateExpansion(bubble, after) + animatorTestRule.advanceTimeBy(1000) + } + getInstrumentation().waitForIdleSync() + + assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue() + + val bbevBottom = bbev.contentBottomOnScreen + bubblePositioner.insets.top + activityScenario.onActivity { + // notify that the IME top coordinate is greater than the bottom of the expanded view. + // there's no overlap so it should not be clipped. + animationHelper.onImeTopChanged(bbevBottom * 2) + } + val outline = Outline() + bbev.outlineProvider.getOutline(bbev, outline) + assertThat(outline.mRect.bottom).isEqualTo(bbev.height) + } + + @Test + fun onImeTopChanged_overlapsWithExpandedView() { + val bubble = createBubble(key = "b1").initialize(container) + val bbev = bubble.bubbleBarExpandedView!! + + val semaphore = Semaphore(0) + val after = Runnable { semaphore.release() } + + activityScenario.onActivity { + bbev.onTaskCreated() + animationHelper.animateExpansion(bubble, after) + animatorTestRule.advanceTimeBy(1000) + } + getInstrumentation().waitForIdleSync() + + assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue() + + activityScenario.onActivity { + // notify that the IME top coordinate is less than the bottom of the expanded view, + // meaning it overlaps with it so we should be clipping the expanded view. + animationHelper.onImeTopChanged(bbev.contentBottomOnScreen - 10) + } + val outline = Outline() + bbev.outlineProvider.getOutline(bbev, outline) + assertThat(outline.mRect.bottom).isEqualTo(bbev.height - 10) + } + private fun createBubble( key: String, taskViewTaskController: TaskViewTaskController = mock<TaskViewTaskController>(), @@ -273,14 +353,24 @@ class BubbleBarAnimationHelperTest { } private fun Bubble.initialize(container: ViewGroup): Bubble { - getInstrumentation().runOnMainSync { container.addView(bubbleBarExpandedView) } + activityScenario.onActivity { container.addView(bubbleBarExpandedView) } // Mark taskView's visible bubbleBarExpandedView!!.onContentVisibilityChanged(true) return this } private fun BubbleOverflow.initialize(container: ViewGroup): BubbleOverflow { - getInstrumentation().runOnMainSync { container.addView(bubbleBarExpandedView) } + activityScenario.onActivity { container.addView(bubbleBarExpandedView) } return this } + + class TestActivity : Activity() { + lateinit var container: FrameLayout + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + container = FrameLayout(applicationContext) + container.layoutParams = LayoutParams(50, 50) + setContentView(container) + } + } } diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt index 5c86b321b60f..9b1645e9534c 100644 --- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt +++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt @@ -37,7 +37,6 @@ import com.android.internal.statusbar.IStatusBarService import com.android.wm.shell.R import com.android.wm.shell.ShellTaskOrganizer import com.android.wm.shell.TestShellExecutor -import com.android.wm.shell.WindowManagerShellWrapper import com.android.wm.shell.bubbles.Bubble import com.android.wm.shell.bubbles.BubbleController import com.android.wm.shell.bubbles.BubbleData @@ -54,6 +53,7 @@ import com.android.wm.shell.bubbles.animation.AnimatableScaleMatrix import com.android.wm.shell.bubbles.properties.BubbleProperties import com.android.wm.shell.bubbles.storage.BubblePersistentRepository import com.android.wm.shell.common.DisplayController +import com.android.wm.shell.common.DisplayImeController import com.android.wm.shell.common.DisplayInsetsController import com.android.wm.shell.common.FloatingContentCoordinator import com.android.wm.shell.common.SyncTransactionQueue @@ -180,7 +180,8 @@ class BubbleBarLayerViewTest { bubbleDataRepository, mock<IStatusBarService>(), windowManager, - WindowManagerShellWrapper(mainExecutor), + mock<DisplayInsetsController>(), + mock<DisplayImeController>(), mock<UserManager>(), mock<LauncherApps>(), bubbleLogger, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java index 9aba3aaa3268..348f13a493b1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java @@ -90,13 +90,15 @@ import com.android.launcher3.icons.BubbleIconFactory; import com.android.wm.shell.Flags; import com.android.wm.shell.R; import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.WindowManagerShellWrapper; import com.android.wm.shell.bubbles.bar.BubbleBarLayerView; import com.android.wm.shell.bubbles.properties.BubbleProperties; import com.android.wm.shell.bubbles.shortcut.BubbleShortcutHelper; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.DisplayImeController; +import com.android.wm.shell.common.DisplayInsetsController; import com.android.wm.shell.common.ExternalInterfaceBinder; import com.android.wm.shell.common.FloatingContentCoordinator; +import com.android.wm.shell.common.ImeListener; import com.android.wm.shell.common.RemoteCallable; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SingleInstanceRemoteListener; @@ -106,7 +108,6 @@ import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.draganddrop.DragAndDropController; import com.android.wm.shell.onehanded.OneHandedController; import com.android.wm.shell.onehanded.OneHandedTransitionCallback; -import com.android.wm.shell.pip.PinnedStackListenerForwarder; import com.android.wm.shell.shared.annotations.ShellBackgroundThread; import com.android.wm.shell.shared.annotations.ShellMainThread; import com.android.wm.shell.shared.bubbles.BubbleBarLocation; @@ -182,7 +183,8 @@ public class BubbleController implements ConfigurationChangeListener, @Nullable private final BubbleStackView.SurfaceSynchronizer mSurfaceSynchronizer; private final FloatingContentCoordinator mFloatingContentCoordinator; private final BubbleDataRepository mDataRepository; - private final WindowManagerShellWrapper mWindowManagerShellWrapper; + private final DisplayInsetsController mDisplayInsetsController; + private final DisplayImeController mDisplayImeController; private final UserManager mUserManager; private final LauncherApps mLauncherApps; private final IStatusBarService mBarService; @@ -291,7 +293,8 @@ public class BubbleController implements ConfigurationChangeListener, BubbleDataRepository dataRepository, @Nullable IStatusBarService statusBarService, WindowManager windowManager, - WindowManagerShellWrapper windowManagerShellWrapper, + DisplayInsetsController displayInsetsController, + DisplayImeController displayImeController, UserManager userManager, LauncherApps launcherApps, BubbleLogger bubbleLogger, @@ -318,7 +321,8 @@ public class BubbleController implements ConfigurationChangeListener, ServiceManager.getService(Context.STATUS_BAR_SERVICE)) : statusBarService; mWindowManager = windowManager; - mWindowManagerShellWrapper = windowManagerShellWrapper; + mDisplayInsetsController = displayInsetsController; + mDisplayImeController = displayImeController; mUserManager = userManager; mFloatingContentCoordinator = floatingContentCoordinator; mDataRepository = dataRepository; @@ -403,11 +407,15 @@ public class BubbleController implements ConfigurationChangeListener, mMainExecutor.execute(() -> removeBubble(bubble.getKey(), DISMISS_INVALID_INTENT)); }); - try { - mWindowManagerShellWrapper.addPinnedStackListener(new BubblesImeListener()); - } catch (RemoteException e) { - e.printStackTrace(); - } + BubblesImeListener bubblesImeListener = + new BubblesImeListener(mDisplayController, mContext.getDisplayId()); + // the insets controller is notified whenever the IME visibility changes whether the IME is + // requested by a bubbled task or non-bubbled task. in the latter case, we need to update + // the position of the stack to avoid overlapping with the IME. + mDisplayInsetsController.addInsetsChangedListener(mContext.getDisplayId(), + bubblesImeListener); + // the ime controller is notified when the IME is requested only by a bubbled task. + mDisplayImeController.addPositionProcessor(bubblesImeListener); mBubbleData.setCurrentUserId(mCurrentUserId); @@ -2515,16 +2523,57 @@ public class BubbleController implements ConfigurationChangeListener, return contextForUser.getPackageManager(); } - /** PinnedStackListener that dispatches IME visibility updates to the stack. */ - //TODO(b/170442945): Better way to do this / insets listener? - private class BubblesImeListener extends PinnedStackListenerForwarder.PinnedTaskListener { + /** {@link ImeListener} that dispatches IME visibility updates to the stack. */ + private class BubblesImeListener extends ImeListener implements + DisplayImeController.ImePositionProcessor { + + BubblesImeListener(DisplayController displayController, int displayId) { + super(displayController, displayId); + } + @Override - public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) { - mBubblePositioner.setImeVisible(imeVisible, imeHeight); + protected void onImeVisibilityChanged(boolean imeVisible, int imeHeight) { + if (getDisplayId() != mContext.getDisplayId()) { + return; + } + // the imeHeight here is actually the ime inset; it only includes the part of the ime + // that overlaps with the Bubbles window. adjust it to include the bottom screen inset, + // so we have the total height of the ime. + int totalImeHeight = imeHeight + mBubblePositioner.getInsets().bottom; + mBubblePositioner.setImeVisible(imeVisible, totalImeHeight); if (mStackView != null) { mStackView.setImeVisible(imeVisible); } } + + @Override + public int onImeStartPositioning(int displayId, int hiddenTop, int shownTop, + boolean showing, boolean isFloating, SurfaceControl.Transaction t) { + if (mContext.getDisplayId() != displayId) { + return IME_ANIMATION_DEFAULT; + } + + if (showing) { + mBubblePositioner.setImeVisible(true, hiddenTop - shownTop); + } else { + mBubblePositioner.setImeVisible(false, 0); + } + if (mStackView != null) { + mStackView.setImeVisible(showing); + } + + return IME_ANIMATION_DEFAULT; + } + + @Override + public void onImePositionChanged(int displayId, int imeTop, SurfaceControl.Transaction t) { + if (mContext.getDisplayId() != displayId) { + return; + } + if (mLayerView != null) { + mLayerView.onImeTopChanged(imeTop); + } + } } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java index de85d9af127d..1a61793eab87 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java @@ -67,6 +67,11 @@ public class BubblePositioner { private @Surface.Rotation int mRotation = Surface.ROTATION_0; private Insets mInsets; private boolean mImeVisible; + /** + * The height of the IME excluding the bottom inset. If the IME is 100 pixels tall and we have + * 20 pixels bottom inset, the IME height is adjusted to 80 to represent the overlap with the + * Bubbles window. + */ private int mImeHeight; private Rect mPositionRect; private int mDefaultMaxBubbles; @@ -336,10 +341,16 @@ public class BubblePositioner { return mImeVisible; } - /** Sets whether the IME is visible. **/ + /** + * Sets whether the IME is visible and its height. + * + * @param visible whether the IME is visible + * @param height the total height of the IME from the bottom of the physical screen + **/ public void setImeVisible(boolean visible, int height) { mImeVisible = visible; - mImeHeight = height; + // adjust the IME to account for the height as seen by the Bubbles window + mImeHeight = visible ? Math.max(height - getInsets().bottom, 0) : 0; } private int getExpandedViewLargeScreenInsetFurthestEdge(boolean isOverflow) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java index 3188e5b9c6d2..de6d1f6c8852 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java @@ -30,6 +30,8 @@ import static com.android.wm.shell.bubbles.bar.BubbleBarExpandedView.TASK_VIEW_A import static com.android.wm.shell.shared.animation.Interpolators.EMPHASIZED; import static com.android.wm.shell.shared.animation.Interpolators.EMPHASIZED_DECELERATE; +import static java.lang.Math.max; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; @@ -375,7 +377,6 @@ public class BubbleBarAnimationHelper { return animator; } - /** * Animate the expanded bubble when it is being dragged */ @@ -586,6 +587,18 @@ public class BubbleBarAnimationHelper { } } + /** Handles IME position changes. */ + public void onImeTopChanged(int imeTop) { + BubbleBarExpandedView bbev = getExpandedView(); + if (bbev == null) { + Log.w(TAG, "Bubble bar expanded view was null when IME top changed"); + return; + } + int bbevBottom = bbev.getContentBottomOnScreen(); + int clip = max(bbevBottom - imeTop, 0); + bbev.updateBottomClip(clip); + } + private @Nullable BubbleBarExpandedView getExpandedView() { BubbleViewProvider bubble = mExpandedBubble; if (bubble != null) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java index 65c929ab6fb4..e073b02dc630 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java @@ -137,6 +137,7 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView private Executor mBackgroundExecutor; private final Rect mSampleRect = new Rect(); private final int[] mLoc = new int[2]; + private final Rect mTempBounds = new Rect(); /** Height of the caption inset at the top of the TaskView */ private int mCaptionHeight; @@ -161,6 +162,9 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView private boolean mIsAnimating; private boolean mIsDragging; + private boolean mIsClipping = false; + private int mBottomClip = 0; + /** An enum value that tracks the visibility state of the task view */ private enum TaskViewVisibilityState { /** The task view is going away, and we're waiting for the surface to be destroyed. */ @@ -203,7 +207,8 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView setOutlineProvider(new ViewOutlineProvider() { @Override public void getOutline(View view, Outline outline) { - outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), mCurrentCornerRadius); + outline.setRoundRect(0, 0, view.getWidth(), view.getHeight() - mBottomClip, + mCurrentCornerRadius); } }); // Set a touch sink to ensure that clicks on the caption area do not propagate to the parent @@ -661,6 +666,52 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView } } + /** The y coordinate of the bottom of the expanded view. */ + public int getContentBottomOnScreen() { + if (mOverflowView != null) { + mOverflowView.getBoundsOnScreen(mTempBounds); + } + if (mTaskView != null) { + mTaskView.getBoundsOnScreen(mTempBounds); + } + // return the bottom of the content rect, adjusted for insets so the result is in screen + // coordinate + return mTempBounds.bottom + mPositioner.getInsets().top; + } + + /** Update the amount by which to clip the expanded view at the bottom. */ + public void updateBottomClip(int bottomClip) { + mBottomClip = bottomClip; + onClipUpdate(); + } + + private void onClipUpdate() { + if (mBottomClip == 0) { + if (mIsClipping) { + mIsClipping = false; + if (mTaskView != null) { + mTaskView.setClipBounds(null); + mTaskView.setEnableSurfaceClipping(false); + } + invalidateOutline(); + } + } else { + if (!mIsClipping) { + mIsClipping = true; + if (mTaskView != null) { + mTaskView.setEnableSurfaceClipping(true); + } + } + invalidateOutline(); + if (mTaskView != null) { + Rect clipBounds = new Rect(0, 0, + mTaskView.getWidth(), + mTaskView.getHeight() - mBottomClip); + mTaskView.setClipBounds(clipBounds); + } + } + } + private void recreateRegionSamplingHelper() { if (mRegionSamplingHelper != null) { mRegionSamplingHelper.stopAndDestroy(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java index 88f34f3043e1..eaa0bd250fc4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java @@ -424,6 +424,13 @@ public class BubbleBarLayerView extends FrameLayout } } + /** Handles IME position changes. */ + public void onImeTopChanged(int imeTop) { + if (mIsExpanded) { + mAnimationHelper.onImeTopChanged(imeTop); + } + } + /** * Log the event only if {@link #mExpandedBubble} is a {@link Bubble}. * <p> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java index 9ebb7f5aa270..eb1e72790a25 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java @@ -419,8 +419,12 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged // already (e.g., when focussing an editText in activity B, while and editText in // activity A is focussed), we will not get a call of #insetsControlChanged, and // therefore have to start the show animation from here - startAnimation(mImeRequestedVisible /* show */, false /* forceRestart */, - statsToken); + if (visible || mImeShowing) { + // only start the animation if we're either already showing or becoming visible. + // otherwise starting another hide animation causes flickers. + startAnimation(mImeRequestedVisible /* show */, false /* forceRestart */, + statsToken); + } // In case of a hide, the statsToken should not been send yet (as the animation // is still ongoing). It will be sent at the end of the animation @@ -723,6 +727,10 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged * Allows other things to synchronize with the ime position */ public interface ImePositionProcessor { + + /** Default animation flags. */ + int IME_ANIMATION_DEFAULT = 0; + /** * Indicates that ime shouldn't animate alpha. It will always be opaque. Used when stuff * behind the IME shouldn't be visible (for example during split-screen adjustment where @@ -732,6 +740,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged /** @hide */ @IntDef(prefix = {"IME_ANIMATION_"}, value = { + IME_ANIMATION_DEFAULT, IME_ANIMATION_NO_ALPHA, }) @interface ImeAnimationFlags { @@ -758,7 +767,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged @ImeAnimationFlags default int onImeStartPositioning(int displayId, int hiddenTop, int shownTop, boolean showing, boolean isFloating, SurfaceControl.Transaction t) { - return 0; + return IME_ANIMATION_DEFAULT; } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ImeListener.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ImeListener.kt index a34d7bed497b..8851b6a2107d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ImeListener.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ImeListener.kt @@ -22,8 +22,8 @@ import android.view.InsetsState import com.android.wm.shell.common.DisplayInsetsController.OnInsetsChangedListener abstract class ImeListener( - private val mDisplayController: DisplayController, - private val mDisplayId: Int + private val displayController: DisplayController, + val displayId: Int ) : OnInsetsChangedListener { // The last insets state private val mInsetsState = InsetsState() @@ -36,17 +36,11 @@ abstract class ImeListener( // Get the stable bounds that account for display cutout and system bars to calculate the // relative IME height - val layout = mDisplayController.getDisplayLayout(mDisplayId) - if (layout == null) { - return - } + val layout = displayController.getDisplayLayout(displayId) ?: return layout.getStableBounds(mTmpBounds) - val wasVisible = getImeVisibilityAndHeight(mInsetsState).first - val oldHeight = getImeVisibilityAndHeight(mInsetsState).second - - val isVisible = getImeVisibilityAndHeight(insetsState).first - val newHeight = getImeVisibilityAndHeight(insetsState).second + val (wasVisible, oldHeight) = getImeVisibilityAndHeight(mInsetsState) + val (isVisible, newHeight) = getImeVisibilityAndHeight(insetsState) mInsetsState.set(insetsState, true) if (wasVisible != isVisible || oldHeight != newHeight) { @@ -54,8 +48,7 @@ abstract class ImeListener( } } - private fun getImeVisibilityAndHeight( - insetsState: InsetsState): Pair<Boolean, Int> { + private fun getImeVisibilityAndHeight(insetsState: InsetsState): Pair<Boolean, Int> { val source = insetsState.peekSource(InsetsSource.ID_IME) val frame = if (source != null && source.isVisible) source.frame else null val height = if (frame != null) mTmpBounds.bottom - frame.top else 0 diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java index d02c6b05e5b6..d8c7f4cbb698 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java @@ -47,7 +47,6 @@ import com.android.launcher3.icons.IconProvider; import com.android.window.flags.Flags; import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.WindowManagerShellWrapper; import com.android.wm.shell.activityembedding.ActivityEmbeddingController; import com.android.wm.shell.apptoweb.AppToWebGenericLinksParser; import com.android.wm.shell.apptoweb.AssistContentRequester; @@ -233,7 +232,8 @@ public abstract class WMShellModule { FloatingContentCoordinator floatingContentCoordinator, IStatusBarService statusBarService, WindowManager windowManager, - WindowManagerShellWrapper windowManagerShellWrapper, + DisplayInsetsController displayInsetsController, + DisplayImeController displayImeController, UserManager userManager, LauncherApps launcherApps, TaskStackListenerImpl taskStackListener, @@ -265,7 +265,8 @@ public abstract class WMShellModule { new BubblePersistentRepository(context)), statusBarService, windowManager, - windowManagerShellWrapper, + displayInsetsController, + displayImeController, userManager, launcherApps, logger, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java index fd387d1811fb..37296531ee34 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java @@ -350,7 +350,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, } cancelPhysicsAnimation(); mMenuController.hideMenu(ANIM_TYPE_DISMISS, false /* resize */); - mPipScheduler.removePipAfterAnimation(); + mPipScheduler.scheduleRemovePip(); } /** Sets the movement bounds to use to constrain PIP position animations. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java index 4461a5c6a70c..7f673d2efc68 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java @@ -122,34 +122,26 @@ public class PipScheduler { * Schedules exit PiP via expand transition. */ public void scheduleExitPipViaExpand() { - WindowContainerTransaction wct = getExitPipViaExpandTransaction(); - if (wct != null) { - mMainExecutor.execute(() -> { + mMainExecutor.execute(() -> { + if (!mPipTransitionState.isInPip()) return; + WindowContainerTransaction wct = getExitPipViaExpandTransaction(); + if (wct != null) { mPipTransitionController.startExitTransition(TRANSIT_EXIT_PIP, wct, null /* destinationBounds */); - }); - } - } - - // TODO: Optimize this by running the animation as part of the transition - /** Runs remove PiP animation and schedules remove PiP transition after the animation ends. */ - public void removePipAfterAnimation() { - SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction(); - PipAlphaAnimator animator = mPipAlphaAnimatorSupplier.get(mContext, - mPipTransitionState.getPinnedTaskLeash(), tx, PipAlphaAnimator.FADE_OUT); - animator.setAnimationEndCallback(this::scheduleRemovePipImmediately); - animator.start(); + } + }); } /** Schedules remove PiP transition. */ - private void scheduleRemovePipImmediately() { - WindowContainerTransaction wct = getRemovePipTransaction(); - if (wct != null) { - mMainExecutor.execute(() -> { + public void scheduleRemovePip() { + mMainExecutor.execute(() -> { + if (!mPipTransitionState.isInPip()) return; + WindowContainerTransaction wct = getRemovePipTransaction(); + if (wct != null) { mPipTransitionController.startExitTransition(TRANSIT_REMOVE_PIP, wct, null /* destinationBounds */); - }); - } + } + }); } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java index acb5622b041c..2e38449d4584 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java @@ -278,7 +278,8 @@ public class PipTransition extends PipTransitionController implements } if (isRemovePipTransition(info)) { - return removePipImmediately(info, startTransaction, finishTransaction, finishCallback); + mPipTransitionState.setState(PipTransitionState.EXITING_PIP); + return startRemoveAnimation(info, startTransaction, finishTransaction, finishCallback); } return false; } @@ -668,13 +669,18 @@ public class PipTransition extends PipTransitionController implements return true; } - private boolean removePipImmediately(@NonNull TransitionInfo info, + private boolean startRemoveAnimation(@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback) { - startTransaction.apply(); - finishCallback.onTransitionFinished(null); - mPipTransitionState.setState(PipTransitionState.EXITED_PIP); + TransitionInfo.Change pipChange = getChangeByToken(info, + mPipTransitionState.getPipTaskToken()); + mFinishCallback = finishCallback; + PipAlphaAnimator animator = new PipAlphaAnimator(mContext, pipChange.getLeash(), + startTransaction, PipAlphaAnimator.FADE_OUT); + finishTransaction.setAlpha(pipChange.getLeash(), 0f); + animator.setAnimationEndCallback(this::finishTransition); + animator.start(); return true; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index 246760e361cd..535112f5124a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -746,6 +746,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "Reordering hide-task to bottom"); wct.reorder(hideTaskToken, false /* onTop */); } + prepareTasksForSplitScreen(new int[] {taskId}, wct); wct.startTask(taskId, options); // If this should be mixed, send the task to avoid split handle transition directly. if (mMixedHandler != null && mMixedHandler.isTaskInPip(taskId, mTaskOrganizer)) { diff --git a/nfc/java/android/nfc/T4tNdefNfceeCcFileInfo.aidl b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/EnterDesktopViaMenuOfStaticOverviewTaskTest.kt index f72f74e8b3b9..fac749be40f1 100644 --- a/nfc/java/android/nfc/T4tNdefNfceeCcFileInfo.aidl +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/EnterDesktopViaMenuOfStaticOverviewTaskTest.kt @@ -14,7 +14,14 @@ * limitations under the License. */ -package android.nfc; +package com.android.wm.shell.functional -parcelable T4tNdefNfceeCcFileInfo; +import android.platform.test.annotations.Postsubmit +import com.android.wm.shell.scenarios.EnterDesktopViaMenuOfStaticOverviewTask +import org.junit.runner.RunWith +import org.junit.runners.BlockJUnit4ClassRunner +/* Functional test for [EnterDesktopViaMenuOfStaticOverviewTask]. */ +@RunWith(BlockJUnit4ClassRunner::class) +@Postsubmit +class EnterDesktopViaMenuOfStaticOverviewTaskTest : EnterDesktopViaMenuOfStaticOverviewTask() diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopViaMenuOfStaticOverviewTask.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopViaMenuOfStaticOverviewTask.kt new file mode 100644 index 000000000000..57647d6aa5df --- /dev/null +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopViaMenuOfStaticOverviewTask.kt @@ -0,0 +1,70 @@ +/* + * Copyright (C) 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 com.android.wm.shell.scenarios + +import android.app.Instrumentation +import android.tools.NavBar +import android.tools.Rotation +import android.tools.traces.parsers.WindowManagerStateHelper +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.uiautomator.UiDevice +import com.android.launcher3.tapl.LauncherInstrumentation +import com.android.server.wm.flicker.helpers.MailAppHelper +import com.android.window.flags.Flags +import com.android.wm.shell.Utils +import org.junit.After +import org.junit.Assume +import org.junit.Before +import org.junit.Ignore +import org.junit.Rule +import org.junit.Test + +@Ignore("Base Test Class") +abstract class EnterDesktopViaMenuOfStaticOverviewTask constructor() { + + private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + private val tapl = LauncherInstrumentation() + private val wmHelper = WindowManagerStateHelper(instrumentation) + private val device = UiDevice.getInstance(instrumentation) + private val mailApp = MailAppHelper(instrumentation) + + @Rule + @JvmField + val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, Rotation.ROTATION_0) + + @Before + fun setup() { + Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet) + // Clear all tasks + val overview = tapl.goHome().switchToOverview() + if (overview.hasTasks()) { + overview.dismissAllTasks() + } + mailApp.open() + tapl.goHome().switchToOverview() + } + + @Test + open fun desktopViaMenuOfStaticOverviewTask() { + tapl.overview.getCurrentTask().tapMenu().tapDesktopMenuItem() + } + + @After + fun teardown() { + mailApp.exit() + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/pip/Android.bp b/libs/WindowManager/Shell/tests/flicker/pip/Android.bp index f40edaebec5e..6e0dcdb21fa8 100644 --- a/libs/WindowManager/Shell/tests/flicker/pip/Android.bp +++ b/libs/WindowManager/Shell/tests/flicker/pip/Android.bp @@ -115,6 +115,11 @@ test_module_config { "com.android.wm.shell.flicker.pip.PipPinchInTest", "com.android.wm.shell.flicker.pip.SetRequestedOrientationWhilePinned", "com.android.wm.shell.flicker.pip.ShowPipAndRotateDisplay", + "com.android.wm.shell.flicker.pip.nonmatchparent.BottomHalfAutoEnterPipOnGoToHomeTest", + "com.android.wm.shell.flicker.pip.nonmatchparent.BottomHalfEnterPipOnUserLeaveHintTest", + "com.android.wm.shell.flicker.pip.nonmatchparent.BottomHalfEnterPipViaAppUiButtonTest", + "com.android.wm.shell.flicker.pip.nonmatchparent.BottomHalfExitPipToAppViaExpandButtonTest", + "com.android.wm.shell.flicker.pip.nonmatchparent.BottomHalfExitPipToAppViaIntentTest", ], test_suites: ["device-tests"], } @@ -266,12 +271,7 @@ test_module_config { test_suites: ["device-tests"], } -test_module_config { - name: "WMShellFlickerTestsPip-nonMatchParent", - base: "WMShellFlickerTestsPip", - include_filters: ["com.android.wm.shell.flicker.pip.nonmatchparent.*"], - test_suites: ["device-tests"], -} +// Not-match Parent test cases test_module_config { name: "WMShellFlickerTestsPip-BottomHalfExitPipToAppViaExpandButtonTest", @@ -287,5 +287,26 @@ test_module_config { test_suites: ["device-tests"], } +test_module_config { + name: "WMShellFlickerTestsPip-BottomHalfAutoEnterPipOnGoToHomeTest", + base: "WMShellFlickerTestsPip", + include_filters: ["com.android.wm.shell.flicker.pip.nonmatchparent.BottomHalfAutoEnterPipOnGoToHomeTest"], + test_suites: ["device-tests"], +} + +test_module_config { + name: "WMShellFlickerTestsPip-BottomHalfEnterPipOnUserLeaveHintTest", + base: "WMShellFlickerTestsPip", + include_filters: ["com.android.wm.shell.flicker.pip.nonmatchparent.BottomHalfEnterPipOnUserLeaveHintTest"], + test_suites: ["device-tests"], +} + +test_module_config { + name: "WMShellFlickerTestsPip-BottomHalfEnterPipViaAppUiButtonTest", + base: "WMShellFlickerTestsPip", + include_filters: ["com.android.wm.shell.flicker.pip.nonmatchparent.BottomHalfEnterPipViaAppUiButtonTest"], + test_suites: ["device-tests"], +} + // End breakdowns for WMShellFlickerTestsPip module //////////////////////////////////////////////////////////////////////////////// diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt index 609a2849f915..84d53d59e7df 100644 --- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt @@ -24,20 +24,16 @@ import android.platform.test.annotations.RequiresFlagsDisabled import android.tools.flicker.junit.FlickerParametersRunnerFactory import android.tools.flicker.legacy.FlickerBuilder import android.tools.flicker.legacy.LegacyFlickerTest -import android.tools.flicker.subject.exceptions.ExceptionMessageBuilder -import android.tools.flicker.subject.exceptions.IncorrectRegionException -import android.tools.flicker.subject.layers.LayerSubject import com.android.server.wm.flicker.helpers.PipAppHelper import com.android.wm.shell.Flags import com.android.wm.shell.flicker.pip.common.EnterPipTransition +import com.android.wm.shell.flicker.pip.common.widthNotSmallerThan import org.junit.Assume import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized -import kotlin.math.abs - /** * Test entering pip from an app via auto-enter property when navigating to home. @@ -77,22 +73,6 @@ open class AutoEnterPipOnGoToHomeTest(flicker: LegacyFlickerTest) : EnterPipTran } } - override val defaultTeardown: FlickerBuilder.() -> Unit = { teardown { pipApp.exit(wmHelper) } } - - private val widthNotSmallerThan: LayerSubject.(LayerSubject) -> Unit = { - val width = visibleRegion.region.bounds.width() - val otherWidth = it.visibleRegion.region.bounds.width() - if (width < otherWidth && abs(width - otherWidth) > EPSILON) { - val errorMsgBuilder = - ExceptionMessageBuilder() - .forSubject(this) - .forIncorrectRegion("width. $width smaller than $otherWidth") - .setExpected(width) - .setActual(otherWidth) - throw IncorrectRegionException(errorMsgBuilder) - } - } - @Postsubmit @Test override fun pipLayerReduces() { @@ -161,9 +141,4 @@ open class AutoEnterPipOnGoToHomeTest(flicker: LegacyFlickerTest) : EnterPipTran override fun visibleLayersShownMoreThanOneConsecutiveEntry() { super.visibleLayersShownMoreThanOneConsecutiveEntry() } - - companion object { - // TODO(b/363080056): A margin of error allowed on certain layer size calculations. - const val EPSILON = 1 - } } diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt index 4399a237bcbb..38f37b4bc1d2 100644 --- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt @@ -88,6 +88,7 @@ class EnterPipOnUserLeaveHintTest(flicker: LegacyFlickerTest) : EnterPipTransiti super.pipOverlayLayerAppearThenDisappear() } + // TODO(b/385086051): check if we can remove optional = true in the test. @Presubmit @Test fun pipAppWindowVisibleChanges() { diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/PipUtils.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/PipUtils.kt new file mode 100644 index 000000000000..3b98d9b99130 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/PipUtils.kt @@ -0,0 +1,39 @@ +/* + * Copyright (C) 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 com.android.wm.shell.flicker.pip.common + +import android.tools.flicker.subject.exceptions.ExceptionMessageBuilder +import android.tools.flicker.subject.exceptions.IncorrectRegionException +import android.tools.flicker.subject.layers.LayerSubject +import kotlin.math.abs + +// TODO(b/363080056): A margin of error allowed on certain layer size calculations. +const val EPSILON = 1 + +internal val widthNotSmallerThan: LayerSubject.(LayerSubject) -> Unit = { + val width = visibleRegion.region.bounds.width() + val otherWidth = it.visibleRegion.region.bounds.width() + if (width < otherWidth && abs(width - otherWidth) > EPSILON) { + val errorMsgBuilder = + ExceptionMessageBuilder() + .forSubject(this) + .forIncorrectRegion("width. $width smaller than $otherWidth") + .setExpected(width) + .setActual(otherWidth) + throw IncorrectRegionException(errorMsgBuilder) + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfAutoEnterPipOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfAutoEnterPipOnGoToHomeTest.kt new file mode 100644 index 000000000000..89f02266ce35 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfAutoEnterPipOnGoToHomeTest.kt @@ -0,0 +1,170 @@ +/* + * Copyright (C) 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 com.android.wm.shell.flicker.pip.nonmatchparent + +import android.platform.test.annotations.Postsubmit +import android.platform.test.annotations.Presubmit +import android.platform.test.annotations.RequiresDevice +import android.platform.test.annotations.RequiresFlagsDisabled +import android.platform.test.annotations.RequiresFlagsEnabled +import android.tools.flicker.junit.FlickerParametersRunnerFactory +import android.tools.flicker.legacy.FlickerBuilder +import android.tools.flicker.legacy.LegacyFlickerTest +import androidx.test.filters.FlakyTest +import com.android.window.flags.Flags +import com.android.wm.shell.flicker.pip.common.widthNotSmallerThan +import org.junit.Assume +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test entering pip from an app with non-match parent layout via auto-enter property when + * navigating to home. + * + * To run this test: `atest WMShellFlickerTestsPip:BottomHalfAutoEnterPipOnGoToHomeTest` + * + * Actions: + * ``` + * Launch [BottomHalfPipLaunchingActivity] in full screen + * Launch [BottomHalfPipActivity] with bottom half layout + * Select "Auto-enter PiP" radio button + * Press Home button or swipe up to go Home and put [BottomHalfPipActivity] in pip mode + * ``` + * + * Notes: + * ``` + * 1. All assertions are inherited from [EnterPipTest] + * 2. Part of the test setup occurs automatically via + * [android.tools.flicker.legacy.runner.TransitionRunner], + * including configuring navigation mode, initial orientation and ensuring no + * apps are running before setup + * ``` + */ +// TODO(b/380796448): re-enable tests after the support of non-match parent PIP animation for PIP2. +@RequiresFlagsDisabled(com.android.wm.shell.Flags.FLAG_ENABLE_PIP2) +@RequiresFlagsEnabled(Flags.FLAG_BETTER_SUPPORT_NON_MATCH_PARENT_ACTIVITY) +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +class BottomHalfAutoEnterPipOnGoToHomeTest(flicker: LegacyFlickerTest) : + BottomHalfEnterPipTransition(flicker) { + + override val thisTransition: FlickerBuilder.() -> Unit = { transitions { + tapl.goHome() + pipApp.waitForPip(wmHelper) + } } + + override val defaultEnterPip: FlickerBuilder.() -> Unit = { + setup { + pipApp.launchViaIntent(wmHelper) + pipApp.enableAutoEnterForPipActivity() + } + } + + @FlakyTest(bugId = 289943985) + @Test + override fun visibleLayersShownMoreThanOneConsecutiveEntry() { + super.visibleLayersShownMoreThanOneConsecutiveEntry() + } + + /* Gestural Navigation */ + + /** + * Checks that [pipApp] window's width is first decreasing then increasing. + * + * In gestural navigation mode, auto entering PiP can initially make the layer smaller before it + * gets larger. + * This tests verifies the width of the PiP layer first decreases and then increases due to + * size and scale animations going to different directions. + * + * Note that we still allow a margin of error of 1px, since around the time + * of handoff between gesture nav task view simulator and + * SwipePipToHomeAnimator, crop can get a bit smaller and scale can get a + * bit larger if swiped aggressively - this can produce off-by-1 errors for + * width too. + */ + @Postsubmit + @Test + fun pipLayerWidthDecreasesThenIncreases() { + Assume.assumeTrue(flicker.scenario.isGesturalNavigation) + flicker.assertLayers { + val pipLayerList = this.layers { pipApp.layerMatchesAnyOf(it) && it.isVisible } + var previousLayer = pipLayerList[0] + var currentLayer = previousLayer + var i = 0 + invoke("layer area is decreasing") { + if (i < pipLayerList.size - 1) { + previousLayer = currentLayer + currentLayer = pipLayerList[++i] + previousLayer.widthNotSmallerThan(currentLayer) + } + }.then().invoke("layer are is increasing", true /* isOptional */) { + if (i < pipLayerList.size - 1) { + previousLayer = currentLayer + currentLayer = pipLayerList[++i] + currentLayer.widthNotSmallerThan(previousLayer) + } + } + } + } + + /* 3-button Navigation */ + + /** + * The PIP layer reduces continuously in 3-Button navigation mode. + */ + @Postsubmit + @Test + override fun pipLayerReduces() { + Assume.assumeFalse(flicker.scenario.isGesturalNavigation) + flicker.assertLayers { + val pipLayerList = this.layers { pipApp.layerMatchesAnyOf(it) && it.isVisible } + pipLayerList.zipWithNext { previous, current -> + current.visibleRegion.notBiggerThan(previous.visibleRegion.region) + } + } + } + + /** Checks that [pipApp] window is animated towards default position in right bottom corner */ + @FlakyTest(bugId = 255578530) + @Test + fun pipLayerMovesTowardsRightBottomCorner() { + // in gestural nav the swipe makes PiP first go upwards + Assume.assumeFalse(flicker.scenario.isGesturalNavigation) + flicker.assertLayers { + val pipLayerList = this.layers { pipApp.layerMatchesAnyOf(it) && it.isVisible } + // Pip animates towards the right bottom corner, but because it is being resized at the + // same time, it is possible it shrinks first quickly below the default position and get + // moved up after that in just few last frames + pipLayerList.zipWithNext { previous, current -> + current.visibleRegion.isToTheRightBottom(previous.visibleRegion.region, 3) + } + } + } + + @Presubmit + @Test + override fun focusChanges() { + // in gestural nav the focus goes to different activity on swipe up + Assume.assumeFalse(flicker.scenario.isGesturalNavigation) + super.focusChanges() + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfEnterPipOnUserLeaveHintTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfEnterPipOnUserLeaveHintTest.kt new file mode 100644 index 000000000000..8642b6c779e4 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfEnterPipOnUserLeaveHintTest.kt @@ -0,0 +1,168 @@ +/* + * Copyright (C) 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 com.android.wm.shell.flicker.pip.nonmatchparent + +import android.platform.test.annotations.Presubmit +import android.platform.test.annotations.RequiresDevice +import android.platform.test.annotations.RequiresFlagsDisabled +import android.platform.test.annotations.RequiresFlagsEnabled +import android.tools.flicker.junit.FlickerParametersRunnerFactory +import android.tools.flicker.legacy.FlickerBuilder +import android.tools.flicker.legacy.LegacyFlickerTest +import com.android.window.flags.Flags +import org.junit.Assume +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test entering pip from an app with bottom half layout via [onUserLeaveHint] and by navigating to + * home. + * + * To run this test: `atest WMShellFlickerTestsPip:BottomHalfEnterPipOnUserLeaveHintTest` + * + * Actions: + * ``` + * Launch [BottomHalfPipLaunchingActivity] in full screen + * Launch [BottomHalfPipActivity] with bottom half layout + * Select "Via code behind" radio button + * Press Home button or swipe up to go Home and put [pipApp] in pip mode + * ``` + * Notes: + * ``` + * 1. All assertions are inherited from [EnterPipTest] + * 2. Part of the test setup occurs automatically via + * [android.tools.flicker.legacy.runner.TransitionRunner], + * including configuring navigation mode, initial orientation and ensuring no + * apps are running before setup + * ``` + */ +// TODO(b/380796448): re-enable tests after the support of non-match parent PIP animation for PIP2. +@RequiresFlagsDisabled(com.android.wm.shell.Flags.FLAG_ENABLE_PIP2) +@RequiresFlagsEnabled(Flags.FLAG_BETTER_SUPPORT_NON_MATCH_PARENT_ACTIVITY) +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +class BottomHalfEnterPipOnUserLeaveHintTest(flicker: LegacyFlickerTest) : + BottomHalfEnterPipTransition(flicker) +{ + override val thisTransition: FlickerBuilder.() -> Unit = { transitions { + tapl.goHome() + pipApp.waitForPip(wmHelper) + } } + + override val defaultEnterPip: FlickerBuilder.() -> Unit = { + setup { + pipApp.launchViaIntent(wmHelper) + pipApp.enableEnterPipOnUserLeaveHint() + } + } + + // gestural navigation + + // TODO(b/385086051): check if we can remove optional = true in the test. + @Presubmit + @Test + fun pipAppWindowVisibleChanges() { + // pip layer in gesture nav will disappear during transition + Assume.assumeTrue(flicker.scenario.isGesturalNavigation) + flicker.assertWm { + this.isAppWindowVisible(pipApp) + .then() + .isAppWindowInvisible(pipApp, isOptional = true) + .then() + .isAppWindowVisible(pipApp, isOptional = true) + } + } + + @Presubmit + @Test + fun pipAppLayerVisibleChanges() { + Assume.assumeTrue(flicker.scenario.isGesturalNavigation) + // pip layer in gesture nav will disappear during transition + flicker.assertLayers { + this.isVisible(pipApp).then().isInvisible(pipApp).then().isVisible(pipApp) + } + } + + @Presubmit + @Test + fun pipLayerRemainInsideVisibleBounds() { + // pip layer in gesture nav will disappear during transition + Assume.assumeTrue(flicker.scenario.isGesturalNavigation) + // pip layer in gesture nav will disappear during transition + flicker.assertLayersStart { this.visibleRegion(pipApp).coversAtMost(displayBounds) } + flicker.assertLayersEnd { this.visibleRegion(pipApp).coversAtMost(displayBounds) } + } + + // 3-button navigation + + @Presubmit + @Test + override fun pipAppWindowAlwaysVisible() { + // In gestural nav the pip will first move behind home and then above home. The visual + // appearance visible->invisible->visible is asserted by pipAppLayerAlwaysVisible(). + // But the internal states of activity don't need to follow that, such as a temporary + // visibility state can be changed quickly outside a transaction so the test doesn't + // detect that. Hence, skip the case to avoid restricting the internal implementation. + Assume.assumeFalse(flicker.scenario.isGesturalNavigation) + super.pipAppWindowAlwaysVisible() + } + + @Presubmit + @Test + override fun pipAppLayerAlwaysVisible() { + // pip layer in gesture nav will disappear during transition + Assume.assumeFalse(flicker.scenario.isGesturalNavigation) + super.pipAppLayerAlwaysVisible() + } + + @Presubmit + @Test + override fun pipOverlayLayerAppearThenDisappear() { + // no overlay in gesture nav for non-auto enter PiP transition + Assume.assumeFalse(flicker.scenario.isGesturalNavigation) + super.pipOverlayLayerAppearThenDisappear() + } + + @Presubmit + @Test + override fun pipLayerReduces() { + // in gestural nav the pip enters through alpha animation + Assume.assumeFalse(flicker.scenario.isGesturalNavigation) + super.pipLayerReduces() + } + + @Presubmit + @Test + override fun focusChanges() { + // in gestural nav the focus goes to different activity on swipe up + Assume.assumeFalse(flicker.scenario.isGesturalNavigation) + super.focusChanges() + } + + @Presubmit + @Test + override fun pipLayerOrOverlayRemainInsideVisibleBounds() { + // pip layer in gesture nav will disappear during transition + Assume.assumeFalse(flicker.scenario.isGesturalNavigation) + super.pipLayerOrOverlayRemainInsideVisibleBounds() + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfEnterPipTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfEnterPipTransition.kt new file mode 100644 index 000000000000..a455de96b25d --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfEnterPipTransition.kt @@ -0,0 +1,46 @@ +/* + * Copyright (C) 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 com.android.wm.shell.flicker.pip.nonmatchparent + +import android.platform.test.annotations.Presubmit +import android.tools.flicker.legacy.LegacyFlickerTest +import com.android.server.wm.flicker.helpers.BottomHalfPipAppHelper +import com.android.server.wm.flicker.helpers.PipAppHelper +import com.android.wm.shell.flicker.pip.common.EnterPipTransition +import org.junit.Test + +/** + * The base class to test enter PIP animation on bottom half activity. + */ +abstract class BottomHalfEnterPipTransition(flicker: LegacyFlickerTest) : + EnterPipTransition(flicker) +{ + override val pipApp: PipAppHelper = BottomHalfPipAppHelper( + instrumentation, + useLaunchingActivity = true + ) + + @Presubmit + @Test + override fun pipWindowRemainInsideVisibleBounds() { + // We only verify the start and end because we update the layout when + // the BottomHalfPipActivity goes to PIP mode, which may lead to intermediate state that + // the window frame may be out of the display frame. + flicker.assertWmStart { visibleRegion(pipApp).coversAtMost(displayBounds) } + flicker.assertLayersEnd { visibleRegion(pipApp).coversAtMost(displayBounds) } + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfEnterPipViaAppUiButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfEnterPipViaAppUiButtonTest.kt new file mode 100644 index 000000000000..cfdd4e550504 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfEnterPipViaAppUiButtonTest.kt @@ -0,0 +1,101 @@ +/* + * Copyright (C) 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 com.android.wm.shell.flicker.pip.nonmatchparent + +import android.platform.test.annotations.Presubmit +import android.platform.test.annotations.RequiresDevice +import android.platform.test.annotations.RequiresFlagsDisabled +import android.platform.test.annotations.RequiresFlagsEnabled +import android.tools.flicker.junit.FlickerParametersRunnerFactory +import android.tools.flicker.legacy.FlickerBuilder +import android.tools.flicker.legacy.LegacyFlickerTest +import android.tools.traces.parsers.toFlickerComponent +import com.android.server.wm.flicker.testapp.ActivityOptions.BottomHalfPip.LAUNCHING_APP_COMPONENT +import com.android.window.flags.Flags +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test entering pip from an app by interacting with the app UI + * + * To run this test: `atest WMShellFlickerTestsPip:BottomHalfEnterPipViaAppUiButtonTest` + * + * Actions: + * ``` + * Launch [BottomHalfPipLaunchingActivity] in full screen + * Launch [BottomHalfPipActivity] with bottom half layout + * Press an "enter pip" button to put [pipApp] in pip mode + * ``` + * + * Notes: + * ``` + * 1. Some default assertions (e.g., nav bar, status bar and screen covered) + * are inherited from [PipTransition] + * 2. Part of the test setup occurs automatically via + * [android.tools.flicker.legacy.runner.TransitionRunner], + * including configuring navigation mode, initial orientation and ensuring no + * apps are running before setup + * ``` + */ +// TODO(b/380796448): re-enable tests after the support of non-match parent PIP animation for PIP2. +@RequiresFlagsDisabled(com.android.wm.shell.Flags.FLAG_ENABLE_PIP2) +@RequiresFlagsEnabled(Flags.FLAG_BETTER_SUPPORT_NON_MATCH_PARENT_ACTIVITY) +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +class BottomHalfEnterPipViaAppUiButtonTest(flicker: LegacyFlickerTest) : + BottomHalfEnterPipTransition(flicker) +{ + override val thisTransition: FlickerBuilder.() -> Unit = { + transitions { pipApp.clickEnterPipButton(wmHelper) } + } + + /** + * Checks if the focus changes to the launching activity behind when the bottom half [pipApp] + * goes to PIP mode. + */ + @Presubmit + @Test + override fun focusChanges() { + flicker.assertEventLog { + this.focusChanges( + pipApp.packageName, + LAUNCHING_APP_COMPONENT.packageName + ) + } + } + + @Presubmit + @Test + override fun launcherLayerBecomesVisible() { + // Disable the test since the background activity is BottomHalfPipLaunchingActivity. + } + + /** + * Checks if the launching activity behind the bottom half [pipApp] is always visible during + * the transition. + */ + @Presubmit + @Test + fun launchingAppLayerAlwaysVisible() { + flicker.assertLayers { isVisible(LAUNCHING_APP_COMPONENT.toFlickerComponent()) } + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleViewInfoTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleViewInfoTest.kt index 1f2eaa6757e8..eeb83df48ab5 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleViewInfoTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleViewInfoTest.kt @@ -33,10 +33,10 @@ import com.android.launcher3.icons.BubbleIconFactory import com.android.wm.shell.ShellTaskOrganizer import com.android.wm.shell.ShellTestCase import com.android.wm.shell.TestShellExecutor -import com.android.wm.shell.WindowManagerShellWrapper import com.android.wm.shell.bubbles.bar.BubbleBarLayerView import com.android.wm.shell.bubbles.properties.BubbleProperties import com.android.wm.shell.common.DisplayController +import com.android.wm.shell.common.DisplayImeController import com.android.wm.shell.common.DisplayInsetsController import com.android.wm.shell.common.FloatingContentCoordinator import com.android.wm.shell.common.ShellExecutor @@ -123,7 +123,8 @@ class BubbleViewInfoTest : ShellTestCase() { mock<BubbleDataRepository>(), mock<IStatusBarService>(), windowManager, - WindowManagerShellWrapper(mainExecutor), + mock<DisplayInsetsController>(), + mock<DisplayImeController>(), mock<UserManager>(), mock<LauncherApps>(), bubbleLogger, diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipSchedulerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipSchedulerTest.java index 3fe8c109807a..a8aa25700c7e 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipSchedulerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipSchedulerTest.java @@ -120,15 +120,22 @@ public class PipSchedulerTest { @Test public void scheduleExitPipViaExpand_nullTaskToken_noop() { setNullPipTaskToken(); + when(mMockPipTransitionState.isInPip()).thenReturn(true); mPipScheduler.scheduleExitPipViaExpand(); - verify(mMockMainExecutor, never()).execute(any()); + verify(mMockMainExecutor, times(1)).execute(mRunnableArgumentCaptor.capture()); + assertNotNull(mRunnableArgumentCaptor.getValue()); + mRunnableArgumentCaptor.getValue().run(); + + verify(mMockPipTransitionController, never()) + .startExitTransition(eq(TRANSIT_EXIT_PIP), any(), isNull()); } @Test public void scheduleExitPipViaExpand_exitTransitionCalled() { setMockPipTaskToken(); + when(mMockPipTransitionState.isInPip()).thenReturn(true); mPipScheduler.scheduleExitPipViaExpand(); @@ -142,20 +149,13 @@ public class PipSchedulerTest { @Test public void removePipAfterAnimation() { - //TODO: Update once this is changed to run animation as part of transition setMockPipTaskToken(); + when(mMockPipTransitionState.isInPip()).thenReturn(true); - mPipScheduler.removePipAfterAnimation(); - verify(mMockAlphaAnimator, times(1)) - .setAnimationEndCallback(mRunnableArgumentCaptor.capture()); - assertNotNull(mRunnableArgumentCaptor.getValue()); - verify(mMockAlphaAnimator, times(1)).start(); - - mRunnableArgumentCaptor.getValue().run(); + mPipScheduler.scheduleRemovePip(); verify(mMockMainExecutor, times(1)).execute(mRunnableArgumentCaptor.capture()); assertNotNull(mRunnableArgumentCaptor.getValue()); - mRunnableArgumentCaptor.getValue().run(); verify(mMockPipTransitionController, times(1)) diff --git a/media/java/android/media/projection/MediaProjection.java b/media/java/android/media/projection/MediaProjection.java index f7f10df5786a..4f7132ad9ab2 100644 --- a/media/java/android/media/projection/MediaProjection.java +++ b/media/java/android/media/projection/MediaProjection.java @@ -324,6 +324,19 @@ public final class MediaProjection { } /** + * Stops projection. + * @hide + */ + public void stop(@StopReason int stopReason) { + try { + Log.d(TAG, "Content Recording: stopping projection"); + mImpl.stop(stopReason); + } catch (RemoteException e) { + Log.e(TAG, "Unable to stop projection", e); + } + } + + /** * Get the underlying IMediaProjection. * @hide */ diff --git a/native/android/OWNERS b/native/android/OWNERS index 1fde7d268517..3ea2d3506f31 100644 --- a/native/android/OWNERS +++ b/native/android/OWNERS @@ -29,6 +29,7 @@ per-file surface_texture.cpp = file:/graphics/java/android/graphics/OWNERS # Input per-file input.cpp = file:/INPUT_OWNERS -# PerformanceHint +# ADPF per-file performance_hint.cpp = file:/ADPF_OWNERS +per-file system_health.cpp = file:/ADPF_OWNERS per-file thermal.cpp = file:/ADPF_OWNERS diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt index 1ccadf90c2a9..f629c8802aef 100644 --- a/native/android/libandroid.map.txt +++ b/native/android/libandroid.map.txt @@ -396,6 +396,7 @@ LIBANDROID { APerformanceHint_notifyWorkloadSpike; # introduced=36 APerformanceHint_borrowSessionFromJava; # introduced=36 APerformanceHint_setNativeSurfaces; # introduced=36 + APerformanceHint_isFeatureSupported; # introduced=36 AWorkDuration_create; # introduced=VanillaIceCream AWorkDuration_release; # introduced=VanillaIceCream AWorkDuration_setWorkPeriodStartTimestampNanos; # introduced=VanillaIceCream @@ -419,7 +420,6 @@ LIBANDROID_PLATFORM { AThermal_setIThermalServiceForTesting; APerformanceHint_setIHintManagerForTesting; APerformanceHint_sendHint; - APerformanceHint_setUseGraphicsPipelineForTesting; APerformanceHint_getThreadIds; APerformanceHint_createSessionInternal; APerformanceHint_createSessionUsingConfigInternal; diff --git a/native/android/performance_hint.cpp b/native/android/performance_hint.cpp index 9257901bcd1f..7e655235732f 100644 --- a/native/android/performance_hint.cpp +++ b/native/android/performance_hint.cpp @@ -71,26 +71,31 @@ using android::base::StringPrintf; struct APerformanceHintSession; -constexpr int64_t SEND_HINT_TIMEOUT = std::chrono::nanoseconds(100ms).count(); struct AWorkDuration : public hal::WorkDuration {}; struct ASessionCreationConfig : public SessionCreationConfig { std::vector<wp<IBinder>> layers{}; - bool hasMode(hal::SessionMode&& mode) { + bool hasMode(hal::SessionMode mode) { return std::find(modesToEnable.begin(), modesToEnable.end(), mode) != modesToEnable.end(); } + void setMode(hal::SessionMode mode, bool enabled) { + if (hasMode(mode)) { + if (!enabled) { + std::erase(modesToEnable, mode); + } + } else if (enabled) { + modesToEnable.push_back(mode); + } + } }; -bool kForceGraphicsPipeline = false; - -bool useGraphicsPipeline() { - return android::os::adpf_graphics_pipeline() || kForceGraphicsPipeline; -} - // A pair of values that determine the behavior of the // load hint rate limiter, to only allow "X hints every Y seconds" -constexpr double kLoadHintInterval = std::chrono::nanoseconds(2s).count(); +constexpr int64_t kLoadHintInterval = std::chrono::nanoseconds(2s).count(); constexpr double kMaxLoadHintsPerInterval = 20; -constexpr double kReplenishRate = kMaxLoadHintsPerInterval / kLoadHintInterval; +// Replenish rate is used for new rate limiting behavior, it currently replenishes at a rate of +// 20 / 2s = 1 per 100us, which is the same limit as before, just enforced differently +constexpr double kReplenishRate = kMaxLoadHintsPerInterval / static_cast<double>(kLoadHintInterval); +constexpr int64_t kSendHintTimeout = kLoadHintInterval / kMaxLoadHintsPerInterval; bool kForceNewHintBehavior = false; template <class T> @@ -149,9 +154,7 @@ private: std::future<bool> mChannelCreationFinished; }; -class SupportInfoWrapper { -public: - SupportInfoWrapper(hal::SupportInfo& info); +struct SupportInfoWrapper : public hal::SupportInfo { bool isSessionModeSupported(hal::SessionMode mode); bool isSessionHintSupported(hal::SessionHint hint); @@ -162,7 +165,6 @@ private: // over that much and cutting off any extra values return (supportBitfield >> static_cast<int>(enumValue)) % 2; } - hal::SupportInfo mSupportInfo; }; class HintManagerClient : public IHintManager::BnHintManagerClient { @@ -188,9 +190,9 @@ public: bool isJava = false); APerformanceHintSession* getSessionFromJava(JNIEnv* _Nonnull env, jobject _Nonnull sessionObj); - APerformanceHintSession* createSessionUsingConfig(ASessionCreationConfig* sessionCreationConfig, - hal::SessionTag tag = hal::SessionTag::APP, - bool isJava = false); + int createSessionUsingConfig(ASessionCreationConfig* sessionCreationConfig, + APerformanceHintSession** sessionPtr, + hal::SessionTag tag = hal::SessionTag::APP, bool isJava = false); int64_t getPreferredRateNanos() const; int32_t getMaxGraphicsPipelineThreadsCount(); FMQWrapper& getFMQWrapper(); @@ -202,6 +204,7 @@ public: std::vector<T>& out); ndk::SpAIBinder& getToken(); SupportInfoWrapper& getSupportInfo(); + bool isFeatureSupported(APerformanceHintFeature feature); private: static APerformanceHintManager* create(std::shared_ptr<IHintManager> iHintManager); @@ -239,8 +242,8 @@ public: int setPreferPowerEfficiency(bool enabled); int reportActualWorkDuration(AWorkDuration* workDuration); bool isJava(); - status_t setNativeSurfaces(ANativeWindow** windows, int numWindows, ASurfaceControl** controls, - int numSurfaceControls); + status_t setNativeSurfaces(ANativeWindow** windows, size_t numWindows, + ASurfaceControl** controls, size_t numSurfaceControls); private: friend struct APerformanceHintManager; @@ -294,14 +297,12 @@ static FMQWrapper& getFMQ() { // ===================================== SupportInfoWrapper implementation -SupportInfoWrapper::SupportInfoWrapper(hal::SupportInfo& info) : mSupportInfo(info) {} - bool SupportInfoWrapper::isSessionHintSupported(hal::SessionHint hint) { - return getEnumSupportFromBitfield(hint, mSupportInfo.sessionHints); + return getEnumSupportFromBitfield(hint, sessionHints); } bool SupportInfoWrapper::isSessionModeSupported(hal::SessionMode mode) { - return getEnumSupportFromBitfield(mode, mSupportInfo.sessionModes); + return getEnumSupportFromBitfield(mode, sessionModes); } // ===================================== APerformanceHintManager implementation @@ -386,12 +387,14 @@ APerformanceHintSession* APerformanceHintManager::createSession( .targetWorkDurationNanos = initialTargetWorkDurationNanos, }}; - return APerformanceHintManager::createSessionUsingConfig(&creationConfig, tag, isJava); + APerformanceHintSession* sessionOut; + APerformanceHintManager::createSessionUsingConfig(&creationConfig, &sessionOut, tag, isJava); + return sessionOut; } -APerformanceHintSession* APerformanceHintManager::createSessionUsingConfig( - ASessionCreationConfig* sessionCreationConfig, hal::SessionTag tag, bool isJava) { - std::shared_ptr<IHintSession> session; +int APerformanceHintManager::createSessionUsingConfig(ASessionCreationConfig* sessionCreationConfig, + APerformanceHintSession** sessionOut, + hal::SessionTag tag, bool isJava) { hal::SessionConfig sessionConfig{.id = -1}; ndk::ScopedAStatus ret; @@ -411,31 +414,65 @@ APerformanceHintSession* APerformanceHintManager::createSessionUsingConfig( } } + bool autoCpu = sessionCreationConfig->hasMode(hal::SessionMode::AUTO_CPU); + bool autoGpu = sessionCreationConfig->hasMode(hal::SessionMode::AUTO_GPU); + + if (autoCpu || autoGpu) { + LOG_ALWAYS_FATAL_IF(!sessionCreationConfig->hasMode(hal::SessionMode::GRAPHICS_PIPELINE), + "Automatic session timing enabled without graphics pipeline mode"); + } + + if (autoCpu && !mSupportInfoWrapper.isSessionModeSupported(hal::SessionMode::AUTO_CPU)) { + ALOGE("Automatic CPU timing enabled but not supported"); + return ENOTSUP; + } + + if (autoGpu && !mSupportInfoWrapper.isSessionModeSupported(hal::SessionMode::AUTO_GPU)) { + ALOGE("Automatic GPU timing enabled but not supported"); + return ENOTSUP; + } + + IHintManager::SessionCreationReturn returnValue; ret = mHintManager->createHintSessionWithConfig(mToken, tag, *static_cast<SessionCreationConfig*>( sessionCreationConfig), - &sessionConfig, &session); + &sessionConfig, &returnValue); sessionCreationConfig->layerTokens.clear(); - if (!ret.isOk() || !session) { + if (!ret.isOk() || !returnValue.session) { ALOGE("%s: PerformanceHint cannot create session. %s", __FUNCTION__, ret.getMessage()); - return nullptr; + switch (ret.getExceptionCode()) { + case binder::Status::EX_UNSUPPORTED_OPERATION: + return ENOTSUP; + case binder::Status::EX_ILLEGAL_ARGUMENT: + return EINVAL; + default: + return EPIPE; + } } - auto out = new APerformanceHintSession(mHintManager, std::move(session), + auto out = new APerformanceHintSession(mHintManager, std::move(returnValue.session), mClientData.preferredRateNanos, sessionCreationConfig->targetWorkDurationNanos, isJava, sessionConfig.id == -1 ? std::nullopt : std::make_optional<hal::SessionConfig>( std::move(sessionConfig))); + + *sessionOut = out; + std::scoped_lock lock(sHintMutex); out->traceThreads(sessionCreationConfig->tids); out->traceTargetDuration(sessionCreationConfig->targetWorkDurationNanos); out->traceModes(sessionCreationConfig->modesToEnable); - return out; + if (returnValue.pipelineThreadLimitExceeded) { + ALOGE("Graphics pipeline session thread limit exceeded!"); + return EBUSY; + } + + return 0; } APerformanceHintSession* APerformanceHintManager::getSessionFromJava(JNIEnv* env, @@ -480,6 +517,25 @@ SupportInfoWrapper& APerformanceHintManager::getSupportInfo() { return mSupportInfoWrapper; } +bool APerformanceHintManager::isFeatureSupported(APerformanceHintFeature feature) { + switch (feature) { + case (APERF_HINT_SESSIONS): + return mSupportInfoWrapper.usesSessions; + case (APERF_HINT_POWER_EFFICIENCY): + return mSupportInfoWrapper.isSessionModeSupported(hal::SessionMode::POWER_EFFICIENCY); + case (APERF_HINT_SURFACE_BINDING): + return mSupportInfoWrapper.compositionData.isSupported; + case (APERF_HINT_GRAPHICS_PIPELINE): + return mSupportInfoWrapper.isSessionModeSupported(hal::SessionMode::GRAPHICS_PIPELINE); + case (APERF_HINT_AUTO_CPU): + return mSupportInfoWrapper.isSessionModeSupported(hal::SessionMode::AUTO_CPU); + case (APERF_HINT_AUTO_GPU): + return mSupportInfoWrapper.isSessionModeSupported(hal::SessionMode::AUTO_GPU); + default: + return false; + } +} + // ===================================== APerformanceHintSession implementation constexpr int kNumEnums = enum_size<hal::SessionHint>(); @@ -512,10 +568,6 @@ APerformanceHintSession::~APerformanceHintSession() { } int APerformanceHintSession::updateTargetWorkDuration(int64_t targetDurationNanos) { - if (targetDurationNanos <= 0) { - ALOGE("%s: targetDurationNanos must be positive", __FUNCTION__); - return EINVAL; - } std::scoped_lock lock(sHintMutex); if (mTargetDurationNanos == targetDurationNanos) { return 0; @@ -546,7 +598,6 @@ int APerformanceHintSession::reportActualWorkDuration(int64_t actualDurationNano .workPeriodStartTimestampNanos = 0, .cpuDurationNanos = actualDurationNanos, .gpuDurationNanos = 0}; - return reportActualWorkDurationInternal(static_cast<AWorkDuration*>(&workDuration)); } @@ -556,17 +607,24 @@ bool APerformanceHintSession::isJava() { int APerformanceHintSession::sendHints(std::vector<hal::SessionHint>& hints, int64_t now, const char*) { - std::scoped_lock lock(sHintMutex); + auto& supportInfo = APerformanceHintManager::getInstance()->getSupportInfo(); + + // Drop all unsupported hints, there's not much point reporting errors or warnings for this + std::erase_if(hints, + [&](hal::SessionHint hint) { return !supportInfo.isSessionHintSupported(hint); }); + if (hints.empty()) { - return EINVAL; + // We successfully sent all hints we were able to, technically + return 0; } + for (auto&& hint : hints) { - if (static_cast<int32_t>(hint) < 0 || static_cast<int32_t>(hint) >= kNumEnums) { - ALOGE("%s: invalid session hint %d", __FUNCTION__, hint); - return EINVAL; - } + LOG_ALWAYS_FATAL_IF(static_cast<int32_t>(hint) < 0 || + static_cast<int32_t>(hint) >= kNumEnums, + "%s: invalid session hint %d", __FUNCTION__, hint); } + std::scoped_lock lock(sHintMutex); if (useNewLoadHintBehavior()) { if (!APerformanceHintManager::getInstance()->canSendLoadHints(hints, now)) { return EBUSY; @@ -575,7 +633,7 @@ int APerformanceHintSession::sendHints(std::vector<hal::SessionHint>& hints, int // keep old rate limiter behavior for legacy flag else { for (auto&& hint : hints) { - if (now < (mLastHintSentTimestamp[static_cast<int32_t>(hint)] + SEND_HINT_TIMEOUT)) { + if (now < (mLastHintSentTimestamp[static_cast<int32_t>(hint)] + kSendHintTimeout)) { return EBUSY; } } @@ -651,7 +709,9 @@ int APerformanceHintSession::setThreads(const int32_t* threadIds, size_t size) { } std::vector<int32_t> tids(threadIds, threadIds + size); ndk::ScopedAStatus ret = mHintManager->setHintSessionThreads(mHintSession, tids); - if (!ret.isOk()) { + + // Illegal state means there were too many graphics pipeline threads + if (!ret.isOk() && ret.getExceptionCode() != EX_SERVICE_SPECIFIC) { ALOGE("%s: failed: %s", __FUNCTION__, ret.getMessage()); if (ret.getExceptionCode() == EX_ILLEGAL_ARGUMENT) { return EINVAL; @@ -663,8 +723,10 @@ int APerformanceHintSession::setThreads(const int32_t* threadIds, size_t size) { std::scoped_lock lock(sHintMutex); traceThreads(tids); + bool tooManyThreads = + ret.getExceptionCode() == EX_SERVICE_SPECIFIC && ret.getServiceSpecificError() == 5; - return 0; + return tooManyThreads ? EBUSY : 0; } int APerformanceHintSession::getThreadIds(int32_t* const threadIds, size_t* size) { @@ -711,10 +773,16 @@ int APerformanceHintSession::reportActualWorkDuration(AWorkDuration* workDuratio int APerformanceHintSession::reportActualWorkDurationInternal(AWorkDuration* workDuration) { int64_t actualTotalDurationNanos = workDuration->durationNanos; - traceActualDuration(workDuration->durationNanos); int64_t now = uptimeNanos(); workDuration->timeStampNanos = now; std::scoped_lock lock(sHintMutex); + + if (mTargetDurationNanos <= 0) { + ALOGE("Cannot report work durations if the target duration is not positive."); + return EINVAL; + } + + traceActualDuration(actualTotalDurationNanos); mActualWorkDurations.push_back(std::move(*workDuration)); if (actualTotalDurationNanos >= mTargetDurationNanos) { @@ -757,9 +825,9 @@ int APerformanceHintSession::reportActualWorkDurationInternal(AWorkDuration* wor return 0; } -status_t APerformanceHintSession::setNativeSurfaces(ANativeWindow** windows, int numWindows, +status_t APerformanceHintSession::setNativeSurfaces(ANativeWindow** windows, size_t numWindows, ASurfaceControl** controls, - int numSurfaceControls) { + size_t numSurfaceControls) { if (!mSessionConfig.has_value()) { return ENOTSUP; } @@ -774,7 +842,10 @@ status_t APerformanceHintSession::setNativeSurfaces(ANativeWindow** windows, int ndkLayerHandles.emplace_back(ndk::SpAIBinder(AIBinder_fromPlatformBinder(handle))); } - mHintSession->associateToLayers(ndkLayerHandles); + auto ret = mHintSession->associateToLayers(ndkLayerHandles); + if (!ret.isOk()) { + return EPIPE; + } return 0; } @@ -857,6 +928,11 @@ bool FMQWrapper::startChannel(IHintManager* manager) { } return true; }); + + // If we're unit testing the FMQ, we should block for it to finish completing + if (gForceFMQEnabled.has_value()) { + mChannelCreationFinished.wait(); + } } return isActive(); } @@ -1029,7 +1105,8 @@ void APerformanceHintSession::traceTargetDuration(int64_t targetDuration) { ATrace_setCounter((mSessionName + " target duration").c_str(), targetDuration); } -// ===================================== C API +// ===================================== Start of C API + APerformanceHintManager* APerformanceHint_getManager() { return APerformanceHintManager::getInstance(); } @@ -1037,10 +1114,16 @@ APerformanceHintManager* APerformanceHint_getManager() { #define VALIDATE_PTR(ptr) \ LOG_ALWAYS_FATAL_IF(ptr == nullptr, "%s: " #ptr " is nullptr", __FUNCTION__); +#define HARD_VALIDATE_INT(value, cmp) \ + LOG_ALWAYS_FATAL_IF(!(value cmp), \ + "%s: Invalid value. Check failed: (" #value " " #cmp \ + ") with value: %" PRIi64, \ + __FUNCTION__, static_cast<int64_t>(value)); + #define VALIDATE_INT(value, cmp) \ if (!(value cmp)) { \ ALOGE("%s: Invalid value. Check failed: (" #value " " #cmp ") with value: %" PRIi64, \ - __FUNCTION__, value); \ + __FUNCTION__, static_cast<int64_t>(value)); \ return EINVAL; \ } @@ -1058,19 +1141,27 @@ APerformanceHintSession* APerformanceHint_createSession(APerformanceHintManager* return manager->createSession(threadIds, size, initialTargetWorkDurationNanos); } -APerformanceHintSession* APerformanceHint_createSessionUsingConfig( - APerformanceHintManager* manager, ASessionCreationConfig* sessionCreationConfig) { +int APerformanceHint_createSessionUsingConfig(APerformanceHintManager* manager, + ASessionCreationConfig* sessionCreationConfig, + APerformanceHintSession** sessionOut) { VALIDATE_PTR(manager); VALIDATE_PTR(sessionCreationConfig); - return manager->createSessionUsingConfig(sessionCreationConfig); + VALIDATE_PTR(sessionOut); + *sessionOut = nullptr; + + return manager->createSessionUsingConfig(sessionCreationConfig, sessionOut); } -APerformanceHintSession* APerformanceHint_createSessionUsingConfigInternal( - APerformanceHintManager* manager, ASessionCreationConfig* sessionCreationConfig, - SessionTag tag) { +int APerformanceHint_createSessionUsingConfigInternal(APerformanceHintManager* manager, + ASessionCreationConfig* sessionCreationConfig, + APerformanceHintSession** sessionOut, + SessionTag tag) { VALIDATE_PTR(manager); VALIDATE_PTR(sessionCreationConfig); - return manager->createSessionUsingConfig(sessionCreationConfig, + VALIDATE_PTR(sessionOut); + *sessionOut = nullptr; + + return manager->createSessionUsingConfig(sessionCreationConfig, sessionOut, static_cast<hal::SessionTag>(tag)); } @@ -1111,6 +1202,7 @@ int APerformanceHint_getMaxGraphicsPipelineThreadsCount(APerformanceHintManager* int APerformanceHint_updateTargetWorkDuration(APerformanceHintSession* session, int64_t targetDurationNanos) { VALIDATE_PTR(session) + VALIDATE_INT(targetDurationNanos, >= 0) return session->updateTargetWorkDuration(targetDurationNanos); } @@ -1204,13 +1296,23 @@ int APerformanceHint_notifyWorkloadSpike(APerformanceHintSession* session, bool } int APerformanceHint_setNativeSurfaces(APerformanceHintSession* session, - ANativeWindow** nativeWindows, int nativeWindowsSize, - ASurfaceControl** surfaceControls, int surfaceControlsSize) { + ANativeWindow** nativeWindows, size_t nativeWindowsSize, + ASurfaceControl** surfaceControls, + size_t surfaceControlsSize) { VALIDATE_PTR(session) return session->setNativeSurfaces(nativeWindows, nativeWindowsSize, surfaceControls, surfaceControlsSize); } +bool APerformanceHint_isFeatureSupported(APerformanceHintFeature feature) { + APerformanceHintManager* manager = APerformanceHintManager::getInstance(); + if (manager == nullptr) { + // Clearly whatever it is isn't supported in this case + return false; + } + return manager->isFeatureSupported(feature); +} + AWorkDuration* AWorkDuration_create() { return new AWorkDuration(); } @@ -1265,78 +1367,32 @@ ASessionCreationConfig* ASessionCreationConfig_create() { void ASessionCreationConfig_release(ASessionCreationConfig* config) { VALIDATE_PTR(config) - delete config; } -int ASessionCreationConfig_setTids(ASessionCreationConfig* config, const pid_t* tids, size_t size) { +void ASessionCreationConfig_setTids(ASessionCreationConfig* config, const pid_t* tids, + size_t size) { VALIDATE_PTR(config) VALIDATE_PTR(tids) + HARD_VALIDATE_INT(size, > 0) - if (!useGraphicsPipeline()) { - return ENOTSUP; - } - - if (size <= 0) { - LOG_ALWAYS_FATAL_IF(size <= 0, - "%s: Invalid value. Thread id list size should be greater than zero.", - __FUNCTION__); - return EINVAL; - } config->tids = std::vector<int32_t>(tids, tids + size); - return 0; } -int ASessionCreationConfig_setTargetWorkDurationNanos(ASessionCreationConfig* config, - int64_t targetWorkDurationNanos) { +void ASessionCreationConfig_setTargetWorkDurationNanos(ASessionCreationConfig* config, + int64_t targetWorkDurationNanos) { VALIDATE_PTR(config) - VALIDATE_INT(targetWorkDurationNanos, >= 0) - - if (!useGraphicsPipeline()) { - return ENOTSUP; - } - config->targetWorkDurationNanos = targetWorkDurationNanos; - return 0; } -int ASessionCreationConfig_setPreferPowerEfficiency(ASessionCreationConfig* config, bool enabled) { +void ASessionCreationConfig_setPreferPowerEfficiency(ASessionCreationConfig* config, bool enabled) { VALIDATE_PTR(config) - - if (!useGraphicsPipeline()) { - return ENOTSUP; - } - - if (enabled) { - config->modesToEnable.push_back(hal::SessionMode::POWER_EFFICIENCY); - } else { - std::erase(config->modesToEnable, hal::SessionMode::POWER_EFFICIENCY); - } - return 0; + config->setMode(hal::SessionMode::POWER_EFFICIENCY, enabled); } -int ASessionCreationConfig_setGraphicsPipeline(ASessionCreationConfig* config, bool enabled) { +void ASessionCreationConfig_setGraphicsPipeline(ASessionCreationConfig* config, bool enabled) { VALIDATE_PTR(config) - - if (!useGraphicsPipeline()) { - return ENOTSUP; - } - - if (enabled) { - config->modesToEnable.push_back(hal::SessionMode::GRAPHICS_PIPELINE); - } else { - std::erase(config->modesToEnable, hal::SessionMode::GRAPHICS_PIPELINE); - - // Remove automatic timing modes if we turn off GRAPHICS_PIPELINE, - // as it is a strict pre-requisite for these to run - std::erase(config->modesToEnable, hal::SessionMode::AUTO_CPU); - std::erase(config->modesToEnable, hal::SessionMode::AUTO_GPU); - } - return 0; -} - -void APerformanceHint_setUseGraphicsPipelineForTesting(bool enabled) { - kForceGraphicsPipeline = enabled; + config->setMode(hal::SessionMode::GRAPHICS_PIPELINE, enabled); } void APerformanceHint_getRateLimiterPropertiesForTesting(int32_t* maxLoadHintsPerInterval, @@ -1349,47 +1405,21 @@ void APerformanceHint_setUseNewLoadHintBehaviorForTesting(bool newBehavior) { kForceNewHintBehavior = newBehavior; } -int ASessionCreationConfig_setNativeSurfaces(ASessionCreationConfig* config, - ANativeWindow** nativeWindows, int nativeWindowsSize, - ASurfaceControl** surfaceControls, - int surfaceControlsSize) { +void ASessionCreationConfig_setNativeSurfaces(ASessionCreationConfig* config, + ANativeWindow** nativeWindows, + size_t nativeWindowsSize, + ASurfaceControl** surfaceControls, + size_t surfaceControlsSize) { VALIDATE_PTR(config) - APerformanceHintManager::layersFromNativeSurfaces<wp<IBinder>>(nativeWindows, nativeWindowsSize, surfaceControls, surfaceControlsSize, config->layers); - - if (config->layers.empty()) { - return EINVAL; - } - - return 0; } -int ASessionCreationConfig_setUseAutoTiming(ASessionCreationConfig* _Nonnull config, bool cpu, - bool gpu) { +void ASessionCreationConfig_setUseAutoTiming(ASessionCreationConfig* _Nonnull config, bool cpu, + bool gpu) { VALIDATE_PTR(config) - if ((cpu || gpu) && !config->hasMode(hal::SessionMode::GRAPHICS_PIPELINE)) { - ALOGE("Automatic timing is not supported unless graphics pipeline mode is enabled first"); - return ENOTSUP; - } - - if (config->hasMode(hal::SessionMode::AUTO_CPU)) { - if (!cpu) { - std::erase(config->modesToEnable, hal::SessionMode::AUTO_CPU); - } - } else if (cpu) { - config->modesToEnable.push_back(static_cast<hal::SessionMode>(hal::SessionMode::AUTO_CPU)); - } - - if (config->hasMode(hal::SessionMode::AUTO_GPU)) { - if (!gpu) { - std::erase(config->modesToEnable, hal::SessionMode::AUTO_GPU); - } - } else if (gpu) { - config->modesToEnable.push_back(static_cast<hal::SessionMode>(hal::SessionMode::AUTO_GPU)); - } - - return 0; + config->setMode(hal::SessionMode::AUTO_CPU, cpu); + config->setMode(hal::SessionMode::AUTO_GPU, gpu); } diff --git a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp index e3c10f63abb4..f68fa1a89540 100644 --- a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp +++ b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp @@ -49,12 +49,87 @@ using HalFlagQueue = ::android::AidlMessageQueue<int8_t, SynchronizedReadWrite>; using namespace android; using namespace testing; +constexpr int64_t DEFAULT_TARGET_NS = 16666666L; + +template <class T, void (*D)(T*)> +std::shared_ptr<T> wrapSP(T* incoming) { + return incoming == nullptr ? nullptr : std::shared_ptr<T>(incoming, [](T* ptr) { D(ptr); }); +} +constexpr auto&& wrapSession = wrapSP<APerformanceHintSession, APerformanceHint_closeSession>; +constexpr auto&& wrapConfig = wrapSP<ASessionCreationConfig, ASessionCreationConfig_release>; +constexpr auto&& wrapWorkDuration = wrapSP<AWorkDuration, AWorkDuration_release>; + +std::shared_ptr<ASessionCreationConfig> createConfig() { + return wrapConfig(ASessionCreationConfig_create()); +} + +struct ConfigCreator { + std::vector<int32_t> tids{1, 2}; + int64_t targetDuration = DEFAULT_TARGET_NS; + bool powerEfficient = false; + bool graphicsPipeline = false; + std::vector<ANativeWindow*> nativeWindows{}; + std::vector<ASurfaceControl*> surfaceControls{}; + bool autoCpu = false; + bool autoGpu = false; +}; + +struct SupportHelper { + bool hintSessions : 1; + bool powerEfficiency : 1; + bool bindToSurface : 1; + bool graphicsPipeline : 1; + bool autoCpu : 1; + bool autoGpu : 1; +}; + +SupportHelper getSupportHelper() { + return { + .hintSessions = APerformanceHint_isFeatureSupported(APERF_HINT_SESSIONS), + .powerEfficiency = APerformanceHint_isFeatureSupported(APERF_HINT_POWER_EFFICIENCY), + .bindToSurface = APerformanceHint_isFeatureSupported(APERF_HINT_SURFACE_BINDING), + .graphicsPipeline = APerformanceHint_isFeatureSupported(APERF_HINT_GRAPHICS_PIPELINE), + .autoCpu = APerformanceHint_isFeatureSupported(APERF_HINT_AUTO_CPU), + .autoGpu = APerformanceHint_isFeatureSupported(APERF_HINT_AUTO_GPU), + }; +} + +SupportHelper getFullySupportedSupportHelper() { + return { + .hintSessions = true, + .powerEfficiency = true, + .graphicsPipeline = true, + .autoCpu = true, + .autoGpu = true, + }; +} + +std::shared_ptr<ASessionCreationConfig> configFromCreator(ConfigCreator&& creator) { + auto config = createConfig(); + + ASessionCreationConfig_setTids(config.get(), creator.tids.data(), creator.tids.size()); + ASessionCreationConfig_setTargetWorkDurationNanos(config.get(), creator.targetDuration); + ASessionCreationConfig_setPreferPowerEfficiency(config.get(), creator.powerEfficient); + ASessionCreationConfig_setGraphicsPipeline(config.get(), creator.graphicsPipeline); + ASessionCreationConfig_setNativeSurfaces(config.get(), + creator.nativeWindows.size() > 0 + ? creator.nativeWindows.data() + : nullptr, + creator.nativeWindows.size(), + creator.surfaceControls.size() > 0 + ? creator.surfaceControls.data() + : nullptr, + creator.surfaceControls.size()); + ASessionCreationConfig_setUseAutoTiming(config.get(), creator.autoCpu, creator.autoGpu); + return config; +} + class MockIHintManager : public IHintManager { public: MOCK_METHOD(ScopedAStatus, createHintSessionWithConfig, (const SpAIBinder& token, hal::SessionTag tag, const SessionCreationConfig& creationConfig, hal::SessionConfig* config, - std::shared_ptr<IHintSession>* _aidl_return), + IHintManager::SessionCreationReturn* _aidl_return), (override)); MOCK_METHOD(ScopedAStatus, setHintSessionThreads, (const std::shared_ptr<IHintSession>& hintSession, @@ -115,8 +190,9 @@ public: APerformanceHint_getRateLimiterPropertiesForTesting(&mMaxLoadHintsPerInterval, &mLoadHintInterval); APerformanceHint_setIHintManagerForTesting(&mMockIHintManager); - APerformanceHint_setUseGraphicsPipelineForTesting(true); APerformanceHint_setUseNewLoadHintBehaviorForTesting(true); + mTids.push_back(1); + mTids.push_back(2); } void TearDown() override { @@ -130,20 +206,22 @@ public: ON_CALL(*mMockIHintManager, registerClient(_, _)) .WillByDefault( DoAll(SetArgPointee<1>(mClientData), [] { return ScopedAStatus::ok(); })); + ON_CALL(*mMockIHintManager, isRemote()).WillByDefault(Return(true)); return APerformanceHint_getManager(); } - APerformanceHintSession* createSession(APerformanceHintManager* manager, - int64_t targetDuration = 56789L, bool isHwui = false) { + void prepareSessionMock() { mMockSession = ndk::SharedRefBase::make<NiceMock<MockIHintSession>>(); const int64_t sessionId = 123; - std::vector<int32_t> tids; - tids.push_back(1); - tids.push_back(2); + + mSessionCreationReturn = IHintManager::SessionCreationReturn{ + .session = mMockSession, + .pipelineThreadLimitExceeded = false, + }; ON_CALL(*mMockIHintManager, createHintSessionWithConfig(_, _, _, _, _)) .WillByDefault(DoAll(SetArgPointee<3>(hal::SessionConfig({.id = sessionId})), - SetArgPointee<4>(std::shared_ptr<IHintSession>(mMockSession)), + SetArgPointee<4>(mSessionCreationReturn), [] { return ScopedAStatus::ok(); })); ON_CALL(*mMockIHintManager, setHintSessionThreads(_, _)).WillByDefault([] { @@ -161,48 +239,36 @@ public: ON_CALL(*mMockSession, reportActualWorkDuration2(_)).WillByDefault([] { return ScopedAStatus::ok(); }); + } + + std::shared_ptr<APerformanceHintSession> createSession(APerformanceHintManager* manager, + int64_t targetDuration = 56789L, + bool isHwui = false) { + prepareSessionMock(); if (isHwui) { - return APerformanceHint_createSessionInternal(manager, tids.data(), tids.size(), - targetDuration, SessionTag::HWUI); + return wrapSession(APerformanceHint_createSessionInternal(manager, mTids.data(), + mTids.size(), targetDuration, + SessionTag::HWUI)); } - return APerformanceHint_createSession(manager, tids.data(), tids.size(), targetDuration); + return wrapSession(APerformanceHint_createSession(manager, mTids.data(), mTids.size(), + targetDuration)); } - APerformanceHintSession* createSessionUsingConfig(APerformanceHintManager* manager, - SessionCreationConfig config, - bool isHwui = false) { - mMockSession = ndk::SharedRefBase::make<NiceMock<MockIHintSession>>(); - const int64_t sessionId = 123; - - ON_CALL(*mMockIHintManager, createHintSessionWithConfig(_, _, _, _, _)) - .WillByDefault(DoAll(SetArgPointee<3>(hal::SessionConfig({.id = sessionId})), - SetArgPointee<4>(std::shared_ptr<IHintSession>(mMockSession)), - [] { return ScopedAStatus::ok(); })); - - ON_CALL(*mMockIHintManager, setHintSessionThreads(_, _)).WillByDefault([] { - return ScopedAStatus::ok(); - }); - ON_CALL(*mMockSession, sendHint(_)).WillByDefault([] { return ScopedAStatus::ok(); }); - ON_CALL(*mMockSession, setMode(_, true)).WillByDefault([] { return ScopedAStatus::ok(); }); - ON_CALL(*mMockSession, close()).WillByDefault([] { return ScopedAStatus::ok(); }); - ON_CALL(*mMockSession, updateTargetWorkDuration(_)).WillByDefault([] { - return ScopedAStatus::ok(); - }); - ON_CALL(*mMockSession, reportActualWorkDuration(_, _)).WillByDefault([] { - return ScopedAStatus::ok(); - }); - ON_CALL(*mMockSession, reportActualWorkDuration2(_)).WillByDefault([] { - return ScopedAStatus::ok(); - }); - + std::shared_ptr<APerformanceHintSession> createSessionUsingConfig( + APerformanceHintManager* manager, std::shared_ptr<ASessionCreationConfig>& config, + bool isHwui = false) { + prepareSessionMock(); + APerformanceHintSession* session; + int out = 0; if (isHwui) { - return APerformanceHint_createSessionUsingConfigInternal( - manager, reinterpret_cast<ASessionCreationConfig*>(&config), SessionTag::HWUI); + out = APerformanceHint_createSessionUsingConfigInternal(manager, config.get(), &session, + SessionTag::HWUI); } - return APerformanceHint_createSessionUsingConfig(manager, - reinterpret_cast<ASessionCreationConfig*>( - &config)); + out = APerformanceHint_createSessionUsingConfig(manager, config.get(), &session); + EXPECT_EQ(out, 0); + + return wrapSession(session); } void setFMQEnabled(bool enabled) { @@ -233,11 +299,13 @@ public: uint32_t mWriteBits = 0x00000002; std::shared_ptr<NiceMock<MockIHintManager>> mMockIHintManager = nullptr; std::shared_ptr<NiceMock<MockIHintSession>> mMockSession = nullptr; + IHintManager::SessionCreationReturn mSessionCreationReturn; std::shared_ptr<AidlMessageQueue<hal::ChannelMessage, SynchronizedReadWrite>> mMockFMQ; std::shared_ptr<AidlMessageQueue<int8_t, SynchronizedReadWrite>> mMockFlagQueue; hardware::EventFlag* mEventFlag; int kMockQueueSize = 20; bool mUsingFMQ = false; + std::vector<int> mTids; IHintManager::HintManagerClientData mClientData{ .powerHalVersion = 6, @@ -273,107 +341,109 @@ bool equalsWithoutTimestamp(hal::WorkDuration lhs, hal::WorkDuration rhs) { TEST_F(PerformanceHintTest, TestSession) { APerformanceHintManager* manager = createManager(); - APerformanceHintSession* session = createSession(manager); + auto&& session = createSession(manager); ASSERT_TRUE(session); int64_t targetDurationNanos = 10; EXPECT_CALL(*mMockSession, updateTargetWorkDuration(Eq(targetDurationNanos))).Times(Exactly(1)); - int result = APerformanceHint_updateTargetWorkDuration(session, targetDurationNanos); + int result = APerformanceHint_updateTargetWorkDuration(session.get(), targetDurationNanos); EXPECT_EQ(0, result); // subsequent call with same target should be ignored but return no error - result = APerformanceHint_updateTargetWorkDuration(session, targetDurationNanos); + result = APerformanceHint_updateTargetWorkDuration(session.get(), targetDurationNanos); EXPECT_EQ(0, result); + Mock::VerifyAndClearExpectations(mMockSession.get()); + usleep(2); // Sleep for longer than preferredUpdateRateNanos. int64_t actualDurationNanos = 20; std::vector<int64_t> actualDurations; actualDurations.push_back(20); EXPECT_CALL(*mMockSession, reportActualWorkDuration2(_)).Times(Exactly(1)); - result = APerformanceHint_reportActualWorkDuration(session, actualDurationNanos); + EXPECT_CALL(*mMockSession, updateTargetWorkDuration(_)).Times(Exactly(1)); + result = APerformanceHint_reportActualWorkDuration(session.get(), actualDurationNanos); EXPECT_EQ(0, result); - result = APerformanceHint_updateTargetWorkDuration(session, -1L); + result = APerformanceHint_reportActualWorkDuration(session.get(), -1L); EXPECT_EQ(EINVAL, result); - result = APerformanceHint_reportActualWorkDuration(session, -1L); + result = APerformanceHint_updateTargetWorkDuration(session.get(), 0); + EXPECT_EQ(0, result); + result = APerformanceHint_updateTargetWorkDuration(session.get(), -2); + EXPECT_EQ(EINVAL, result); + result = APerformanceHint_reportActualWorkDuration(session.get(), 12L); EXPECT_EQ(EINVAL, result); SessionHint hintId = SessionHint::CPU_LOAD_RESET; EXPECT_CALL(*mMockSession, sendHint(Eq(hintId))).Times(Exactly(1)); - result = APerformanceHint_sendHint(session, hintId); + result = APerformanceHint_sendHint(session.get(), hintId); EXPECT_EQ(0, result); EXPECT_CALL(*mMockSession, sendHint(Eq(SessionHint::CPU_LOAD_UP))).Times(Exactly(1)); - result = APerformanceHint_notifyWorkloadIncrease(session, true, false, "Test hint"); + result = APerformanceHint_notifyWorkloadIncrease(session.get(), true, false, "Test hint"); EXPECT_EQ(0, result); EXPECT_CALL(*mMockSession, sendHint(Eq(SessionHint::CPU_LOAD_RESET))).Times(Exactly(1)); EXPECT_CALL(*mMockSession, sendHint(Eq(SessionHint::GPU_LOAD_RESET))).Times(Exactly(1)); - result = APerformanceHint_notifyWorkloadReset(session, true, true, "Test hint"); + result = APerformanceHint_notifyWorkloadReset(session.get(), true, true, "Test hint"); EXPECT_EQ(0, result); EXPECT_CALL(*mMockSession, sendHint(Eq(SessionHint::CPU_LOAD_SPIKE))).Times(Exactly(1)); EXPECT_CALL(*mMockSession, sendHint(Eq(SessionHint::GPU_LOAD_SPIKE))).Times(Exactly(1)); - result = APerformanceHint_notifyWorkloadSpike(session, true, true, "Test hint"); + result = APerformanceHint_notifyWorkloadSpike(session.get(), true, true, "Test hint"); EXPECT_EQ(0, result); - result = APerformanceHint_sendHint(session, static_cast<SessionHint>(-1)); - EXPECT_EQ(EINVAL, result); + EXPECT_DEATH( + { APerformanceHint_sendHint(session.get(), static_cast<SessionHint>(-1)); }, + "invalid session hint"); Mock::VerifyAndClearExpectations(mMockSession.get()); for (int i = 0; i < mMaxLoadHintsPerInterval; ++i) { - APerformanceHint_sendHint(session, hintId); + APerformanceHint_sendHint(session.get(), hintId); } // Expect to get rate limited if we try to send faster than the limiter allows EXPECT_CALL(*mMockSession, sendHint(_)).Times(Exactly(0)); - result = APerformanceHint_notifyWorkloadIncrease(session, true, true, "Test hint"); + result = APerformanceHint_notifyWorkloadIncrease(session.get(), true, true, "Test hint"); EXPECT_EQ(result, EBUSY); EXPECT_CALL(*mMockSession, sendHint(_)).Times(Exactly(0)); - result = APerformanceHint_notifyWorkloadReset(session, true, true, "Test hint"); + result = APerformanceHint_notifyWorkloadReset(session.get(), true, true, "Test hint"); EXPECT_CALL(*mMockSession, close()).Times(Exactly(1)); - APerformanceHint_closeSession(session); } TEST_F(PerformanceHintTest, TestUpdatedSessionCreation) { EXPECT_CALL(*mMockIHintManager, createHintSessionWithConfig(_, _, _, _, _)).Times(1); APerformanceHintManager* manager = createManager(); - APerformanceHintSession* session = createSession(manager); + auto&& session = createSession(manager); ASSERT_TRUE(session); - APerformanceHint_closeSession(session); } TEST_F(PerformanceHintTest, TestSessionCreationUsingConfig) { EXPECT_CALL(*mMockIHintManager, createHintSessionWithConfig(_, _, _, _, _)).Times(1); - SessionCreationConfig config{.tids = std::vector<int32_t>(1, 2), - .targetWorkDurationNanos = 5678, - .modesToEnable = std::vector<hal::SessionMode>(0)}; + auto&& config = configFromCreator({.tids = mTids}); APerformanceHintManager* manager = createManager(); - APerformanceHintSession* session = createSessionUsingConfig(manager, config); + auto&& session = createSessionUsingConfig(manager, config); ASSERT_TRUE(session); - APerformanceHint_closeSession(session); } TEST_F(PerformanceHintTest, TestHwuiSessionCreation) { EXPECT_CALL(*mMockIHintManager, createHintSessionWithConfig(_, hal::SessionTag::HWUI, _, _, _)) .Times(1); APerformanceHintManager* manager = createManager(); - APerformanceHintSession* session = createSession(manager, 56789L, true); + auto&& session = createSession(manager, 56789L, true); ASSERT_TRUE(session); - APerformanceHint_closeSession(session); } TEST_F(PerformanceHintTest, SetThreads) { APerformanceHintManager* manager = createManager(); - APerformanceHintSession* session = createSession(manager); + auto&& session = createSession(manager); ASSERT_TRUE(session); int32_t emptyTids[2]; - int result = APerformanceHint_setThreads(session, emptyTids, 0); + int result = APerformanceHint_setThreads(session.get(), emptyTids, 0); EXPECT_EQ(EINVAL, result); std::vector<int32_t> newTids; newTids.push_back(1); newTids.push_back(3); EXPECT_CALL(*mMockIHintManager, setHintSessionThreads(_, Eq(newTids))).Times(Exactly(1)); - result = APerformanceHint_setThreads(session, newTids.data(), newTids.size()); + result = APerformanceHint_setThreads(session.get(), newTids.data(), newTids.size()); EXPECT_EQ(0, result); testing::Mock::VerifyAndClearExpectations(mMockIHintManager.get()); @@ -383,27 +453,27 @@ TEST_F(PerformanceHintTest, SetThreads) { EXPECT_CALL(*mMockIHintManager, setHintSessionThreads(_, Eq(invalidTids))) .Times(Exactly(1)) .WillOnce(Return(ByMove(ScopedAStatus::fromExceptionCode(EX_SECURITY)))); - result = APerformanceHint_setThreads(session, invalidTids.data(), invalidTids.size()); + result = APerformanceHint_setThreads(session.get(), invalidTids.data(), invalidTids.size()); EXPECT_EQ(EPERM, result); } TEST_F(PerformanceHintTest, SetPowerEfficient) { APerformanceHintManager* manager = createManager(); - APerformanceHintSession* session = createSession(manager); + auto&& session = createSession(manager); ASSERT_TRUE(session); EXPECT_CALL(*mMockSession, setMode(_, Eq(true))).Times(Exactly(1)); - int result = APerformanceHint_setPreferPowerEfficiency(session, true); + int result = APerformanceHint_setPreferPowerEfficiency(session.get(), true); EXPECT_EQ(0, result); EXPECT_CALL(*mMockSession, setMode(_, Eq(false))).Times(Exactly(1)); - result = APerformanceHint_setPreferPowerEfficiency(session, false); + result = APerformanceHint_setPreferPowerEfficiency(session.get(), false); EXPECT_EQ(0, result); } TEST_F(PerformanceHintTest, CreateZeroTargetDurationSession) { APerformanceHintManager* manager = createManager(); - APerformanceHintSession* session = createSession(manager, 0); + auto&& session = createSession(manager, 0); ASSERT_TRUE(session); } @@ -428,12 +498,12 @@ MATCHER_P(WorkDurationEq, expected, "") { TEST_F(PerformanceHintTest, TestAPerformanceHint_reportActualWorkDuration2) { APerformanceHintManager* manager = createManager(); - APerformanceHintSession* session = createSession(manager); + auto&& session = createSession(manager); ASSERT_TRUE(session); int64_t targetDurationNanos = 10; EXPECT_CALL(*mMockSession, updateTargetWorkDuration(Eq(targetDurationNanos))).Times(Exactly(1)); - int result = APerformanceHint_updateTargetWorkDuration(session, targetDurationNanos); + int result = APerformanceHint_updateTargetWorkDuration(session.get(), targetDurationNanos); EXPECT_EQ(0, result); usleep(2); // Sleep for longer than preferredUpdateRateNanos. @@ -452,54 +522,53 @@ TEST_F(PerformanceHintTest, TestAPerformanceHint_reportActualWorkDuration2) { EXPECT_CALL(*mMockSession, reportActualWorkDuration2(WorkDurationEq(actualWorkDurations))) .Times(Exactly(pair.expectedResult == OK)); - result = APerformanceHint_reportActualWorkDuration2(session, + result = APerformanceHint_reportActualWorkDuration2(session.get(), reinterpret_cast<AWorkDuration*>( &pair.duration)); EXPECT_EQ(pair.expectedResult, result); } EXPECT_CALL(*mMockSession, close()).Times(Exactly(1)); - APerformanceHint_closeSession(session); } TEST_F(PerformanceHintTest, TestAWorkDuration) { - AWorkDuration* aWorkDuration = AWorkDuration_create(); + // AWorkDuration* aWorkDuration = AWorkDuration_create(); + auto&& aWorkDuration = wrapWorkDuration(AWorkDuration_create()); ASSERT_NE(aWorkDuration, nullptr); - AWorkDuration_setWorkPeriodStartTimestampNanos(aWorkDuration, 1); - AWorkDuration_setActualTotalDurationNanos(aWorkDuration, 20); - AWorkDuration_setActualCpuDurationNanos(aWorkDuration, 13); - AWorkDuration_setActualGpuDurationNanos(aWorkDuration, 8); - AWorkDuration_release(aWorkDuration); + AWorkDuration_setWorkPeriodStartTimestampNanos(aWorkDuration.get(), 1); + AWorkDuration_setActualTotalDurationNanos(aWorkDuration.get(), 20); + AWorkDuration_setActualCpuDurationNanos(aWorkDuration.get(), 13); + AWorkDuration_setActualGpuDurationNanos(aWorkDuration.get(), 8); } TEST_F(PerformanceHintTest, TestCreateUsingFMQ) { setFMQEnabled(true); APerformanceHintManager* manager = createManager(); - APerformanceHintSession* session = createSession(manager); + auto&& session = createSession(manager); ASSERT_TRUE(session); } TEST_F(PerformanceHintTest, TestUpdateTargetWorkDurationUsingFMQ) { setFMQEnabled(true); APerformanceHintManager* manager = createManager(); - APerformanceHintSession* session = createSession(manager); - APerformanceHint_updateTargetWorkDuration(session, 456); + auto&& session = createSession(manager); + APerformanceHint_updateTargetWorkDuration(session.get(), 456); expectToReadFromFmq<HalChannelMessageContents::Tag::targetDuration>(456); } TEST_F(PerformanceHintTest, TestSendHintUsingFMQ) { setFMQEnabled(true); APerformanceHintManager* manager = createManager(); - APerformanceHintSession* session = createSession(manager); - APerformanceHint_sendHint(session, SessionHint::CPU_LOAD_UP); + auto&& session = createSession(manager); + APerformanceHint_sendHint(session.get(), SessionHint::CPU_LOAD_UP); expectToReadFromFmq<HalChannelMessageContents::Tag::hint>(hal::SessionHint::CPU_LOAD_UP); } TEST_F(PerformanceHintTest, TestReportActualUsingFMQ) { setFMQEnabled(true); APerformanceHintManager* manager = createManager(); - APerformanceHintSession* session = createSession(manager); + auto&& session = createSession(manager); hal::WorkDuration duration{.timeStampNanos = 3, .durationNanos = 999999, .workPeriodStartTimestampNanos = 1, @@ -513,20 +582,91 @@ TEST_F(PerformanceHintTest, TestReportActualUsingFMQ) { .gpuDurationNanos = duration.gpuDurationNanos, }; - APerformanceHint_reportActualWorkDuration2(session, + APerformanceHint_reportActualWorkDuration2(session.get(), reinterpret_cast<AWorkDuration*>(&duration)); expectToReadFromFmq<HalChannelMessageContents::Tag::workDuration>(durationExpected); } TEST_F(PerformanceHintTest, TestASessionCreationConfig) { - ASessionCreationConfig* config = ASessionCreationConfig_create(); + auto&& config = configFromCreator({ + .tids = mTids, + .targetDuration = 20, + .powerEfficient = true, + .graphicsPipeline = true, + }); + + APerformanceHintManager* manager = createManager(); + auto&& session = createSessionUsingConfig(manager, config); + + ASSERT_NE(session, nullptr); ASSERT_NE(config, nullptr); +} + +TEST_F(PerformanceHintTest, TestSupportObject) { + // Disable GPU and Power Efficiency support to test partial enabling + mClientData.supportInfo.sessionModes &= ~(1 << (int)hal::SessionMode::AUTO_GPU); + mClientData.supportInfo.sessionHints &= ~(1 << (int)hal::SessionHint::GPU_LOAD_UP); + mClientData.supportInfo.sessionHints &= ~(1 << (int)hal::SessionHint::POWER_EFFICIENCY); + + APerformanceHintManager* manager = createManager(); + + union { + int expectedSupportInt; + SupportHelper expectedSupport; + }; + + union { + int actualSupportInt; + SupportHelper actualSupport; + }; + + expectedSupport = getFullySupportedSupportHelper(); + actualSupport = getSupportHelper(); + + expectedSupport.autoGpu = false; + + EXPECT_EQ(expectedSupportInt, actualSupportInt); +} + +TEST_F(PerformanceHintTest, TestCreatingAutoSession) { + // Disable GPU capability for testing + mClientData.supportInfo.sessionModes &= ~(1 << (int)hal::SessionMode::AUTO_GPU); + APerformanceHintManager* manager = createManager(); - const int32_t testTids[2] = {1, 2}; - const size_t size = 2; - EXPECT_EQ(ASessionCreationConfig_setTids(config, testTids, size), 0); - EXPECT_EQ(ASessionCreationConfig_setTargetWorkDurationNanos(config, 20), 0); - EXPECT_EQ(ASessionCreationConfig_setPreferPowerEfficiency(config, true), 0); - EXPECT_EQ(ASessionCreationConfig_setGraphicsPipeline(config, true), 0); - ASessionCreationConfig_release(config); + auto&& invalidConfig = configFromCreator({ + .tids = mTids, + .targetDuration = 20, + .graphicsPipeline = false, + .autoCpu = true, + .autoGpu = true, + }); + + EXPECT_DEATH({ createSessionUsingConfig(manager, invalidConfig); }, ""); + + auto&& unsupportedConfig = configFromCreator({ + .tids = mTids, + .targetDuration = 20, + .graphicsPipeline = true, + .autoCpu = true, + .autoGpu = true, + }); + + APerformanceHintSession* unsupportedSession = nullptr; + + // Creating a session with auto timing but no graphics pipeline should fail + int out = APerformanceHint_createSessionUsingConfig(manager, unsupportedConfig.get(), + &unsupportedSession); + EXPECT_EQ(out, ENOTSUP); + EXPECT_EQ(wrapSession(unsupportedSession), nullptr); + + auto&& validConfig = configFromCreator({ + .tids = mTids, + .targetDuration = 20, + .graphicsPipeline = true, + .autoCpu = true, + .autoGpu = false, + }); + + auto&& validSession = createSessionUsingConfig(manager, validConfig); + EXPECT_NE(validSession, nullptr); } diff --git a/nfc/Android.bp b/nfc/Android.bp deleted file mode 100644 index 0fdb3bd38db8..000000000000 --- a/nfc/Android.bp +++ /dev/null @@ -1,79 +0,0 @@ -package { - default_team: "trendy_team_fwk_nfc", - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_base_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_base_license"], -} - -filegroup { - name: "framework-nfc-updatable-sources", - path: "java", - srcs: [ - "java/**/*.java", - "java/**/*.aidl", - ], - visibility: [ - "//frameworks/base:__subpackages__", - "//packages/apps/Nfc:__subpackages__", - "//packages/modules/Nfc:__subpackages__", - ], -} - -java_sdk_library { - name: "framework-nfc", - libs: [ - "androidx.annotation_annotation", - "unsupportedappusage", // for android.compat.annotation.UnsupportedAppUsage - "framework-permission-s.stubs.module_lib", - "framework-permission.stubs.module_lib", - ], - stub_only_libs: [ - // Needed for javadoc references. - "framework-permission-s.stubs.module_lib", - ], - static_libs: [ - "android.nfc.flags-aconfig-java", - "android.permission.flags-aconfig-java", - ], - srcs: [ - ":framework-nfc-updatable-sources", - ":framework-nfc-javastream-protos", - ], - defaults: ["framework-module-defaults"], - sdk_version: "module_current", - min_sdk_version: "35", // Make it 36 once available. - installable: true, - optimize: { - enabled: false, - }, - hostdex: true, // for hiddenapi check - permitted_packages: [ - "android.nfc", - "com.android.nfc", - ], - impl_library_visibility: [ - "//frameworks/base:__subpackages__", - "//cts:__subpackages__", - "//packages/apps/Nfc:__subpackages__", - "//packages/modules/Nfc:__subpackages__", - ], - jarjar_rules: ":nfc-jarjar-rules", - lint: { - baseline_filename: "lint-baseline.xml", - }, - apex_available: [ - "//apex_available:platform", - "com.android.nfcservices", - ], - aconfig_declarations: [ - "android.nfc.flags-aconfig", - ], -} - -filegroup { - name: "nfc-jarjar-rules", - srcs: ["jarjar-rules.txt"], -} diff --git a/nfc/OWNERS b/nfc/OWNERS deleted file mode 100644 index f46dccd97974..000000000000 --- a/nfc/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -# Bug component: 48448 -include platform/packages/apps/Nfc:/OWNERS
\ No newline at end of file diff --git a/nfc/TEST_MAPPING b/nfc/TEST_MAPPING deleted file mode 100644 index 49c778d22038..000000000000 --- a/nfc/TEST_MAPPING +++ /dev/null @@ -1,13 +0,0 @@ -{ - "presubmit": [ - { - "name": "NfcManagerTests" - }, - { - "name": "CtsNfcTestCases" - }, - { - "name": "CtsNdefTestCases" - } - ] -} diff --git a/nfc/api/current.txt b/nfc/api/current.txt deleted file mode 100644 index c8c479a4d2ad..000000000000 --- a/nfc/api/current.txt +++ /dev/null @@ -1,495 +0,0 @@ -// Signature format: 2.0 -package android.nfc { - - public final class AvailableNfcAntenna implements android.os.Parcelable { - ctor public AvailableNfcAntenna(int, int); - method public int describeContents(); - method public int getLocationX(); - method public int getLocationY(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.nfc.AvailableNfcAntenna> CREATOR; - } - - public class FormatException extends java.lang.Exception { - ctor public FormatException(); - ctor public FormatException(String); - ctor public FormatException(String, Throwable); - } - - public final class NdefMessage implements android.os.Parcelable { - ctor public NdefMessage(byte[]) throws android.nfc.FormatException; - ctor public NdefMessage(android.nfc.NdefRecord, android.nfc.NdefRecord...); - ctor public NdefMessage(android.nfc.NdefRecord[]); - method public int describeContents(); - method public int getByteArrayLength(); - method public android.nfc.NdefRecord[] getRecords(); - method public byte[] toByteArray(); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.nfc.NdefMessage> CREATOR; - } - - public final class NdefRecord implements android.os.Parcelable { - ctor public NdefRecord(short, byte[], byte[], byte[]); - ctor @Deprecated public NdefRecord(byte[]) throws android.nfc.FormatException; - method public static android.nfc.NdefRecord createApplicationRecord(String); - method public static android.nfc.NdefRecord createExternal(String, String, byte[]); - method public static android.nfc.NdefRecord createMime(String, byte[]); - method public static android.nfc.NdefRecord createTextRecord(String, String); - method public static android.nfc.NdefRecord createUri(android.net.Uri); - method public static android.nfc.NdefRecord createUri(String); - method public int describeContents(); - method public byte[] getId(); - method public byte[] getPayload(); - method public short getTnf(); - method public byte[] getType(); - method @Deprecated public byte[] toByteArray(); - method public String toMimeType(); - method public android.net.Uri toUri(); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.nfc.NdefRecord> CREATOR; - field public static final byte[] RTD_ALTERNATIVE_CARRIER; - field public static final byte[] RTD_HANDOVER_CARRIER; - field public static final byte[] RTD_HANDOVER_REQUEST; - field public static final byte[] RTD_HANDOVER_SELECT; - field public static final byte[] RTD_SMART_POSTER; - field public static final byte[] RTD_TEXT; - field public static final byte[] RTD_URI; - field public static final short TNF_ABSOLUTE_URI = 3; // 0x3 - field public static final short TNF_EMPTY = 0; // 0x0 - field public static final short TNF_EXTERNAL_TYPE = 4; // 0x4 - field public static final short TNF_MIME_MEDIA = 2; // 0x2 - field public static final short TNF_UNCHANGED = 6; // 0x6 - field public static final short TNF_UNKNOWN = 5; // 0x5 - field public static final short TNF_WELL_KNOWN = 1; // 0x1 - } - - public final class NfcAdapter { - method @FlaggedApi("android.nfc.nfc_state_change") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean disable(); - method public void disableForegroundDispatch(android.app.Activity); - method public void disableReaderMode(android.app.Activity); - method @FlaggedApi("android.nfc.nfc_state_change") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enable(); - method public void enableForegroundDispatch(android.app.Activity, android.app.PendingIntent, android.content.IntentFilter[], String[][]); - method public void enableReaderMode(android.app.Activity, android.nfc.NfcAdapter.ReaderCallback, int, android.os.Bundle); - method public static android.nfc.NfcAdapter getDefaultAdapter(android.content.Context); - method @Nullable public android.nfc.NfcAntennaInfo getNfcAntennaInfo(); - method @FlaggedApi("android.nfc.enable_nfc_charging") @Nullable public android.nfc.WlcListenerDeviceInfo getWlcListenerDeviceInfo(); - method public boolean ignore(android.nfc.Tag, int, android.nfc.NfcAdapter.OnTagRemovedListener, android.os.Handler); - method public boolean isEnabled(); - method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean isObserveModeEnabled(); - method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean isObserveModeSupported(); - method @FlaggedApi("android.nfc.enable_nfc_reader_option") public boolean isReaderOptionEnabled(); - method @FlaggedApi("android.nfc.enable_nfc_reader_option") public boolean isReaderOptionSupported(); - method public boolean isSecureNfcEnabled(); - method public boolean isSecureNfcSupported(); - method @FlaggedApi("android.nfc.nfc_check_tag_intent_preference") public boolean isTagIntentAllowed(); - method @FlaggedApi("android.nfc.nfc_check_tag_intent_preference") public boolean isTagIntentAppPreferenceSupported(); - method @FlaggedApi("android.nfc.enable_nfc_charging") public boolean isWlcEnabled(); - method @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public void resetDiscoveryTechnology(@NonNull android.app.Activity); - method @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public void setDiscoveryTechnology(@NonNull android.app.Activity, int, int); - method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean setObserveModeEnabled(boolean); - field public static final String ACTION_ADAPTER_STATE_CHANGED = "android.nfc.action.ADAPTER_STATE_CHANGED"; - field @FlaggedApi("android.nfc.nfc_check_tag_intent_preference") public static final String ACTION_CHANGE_TAG_INTENT_PREFERENCE = "android.nfc.action.CHANGE_TAG_INTENT_PREFERENCE"; - field public static final String ACTION_NDEF_DISCOVERED = "android.nfc.action.NDEF_DISCOVERED"; - field @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public static final String ACTION_PREFERRED_PAYMENT_CHANGED = "android.nfc.action.PREFERRED_PAYMENT_CHANGED"; - field public static final String ACTION_TAG_DISCOVERED = "android.nfc.action.TAG_DISCOVERED"; - field public static final String ACTION_TECH_DISCOVERED = "android.nfc.action.TECH_DISCOVERED"; - field @RequiresPermission(android.Manifest.permission.NFC_TRANSACTION_EVENT) public static final String ACTION_TRANSACTION_DETECTED = "android.nfc.action.TRANSACTION_DETECTED"; - field public static final String EXTRA_ADAPTER_STATE = "android.nfc.extra.ADAPTER_STATE"; - field public static final String EXTRA_AID = "android.nfc.extra.AID"; - field public static final String EXTRA_DATA = "android.nfc.extra.DATA"; - field public static final String EXTRA_ID = "android.nfc.extra.ID"; - field public static final String EXTRA_NDEF_MESSAGES = "android.nfc.extra.NDEF_MESSAGES"; - field public static final String EXTRA_PREFERRED_PAYMENT_CHANGED_REASON = "android.nfc.extra.PREFERRED_PAYMENT_CHANGED_REASON"; - field public static final String EXTRA_READER_PRESENCE_CHECK_DELAY = "presence"; - field public static final String EXTRA_SECURE_ELEMENT_NAME = "android.nfc.extra.SECURE_ELEMENT_NAME"; - field public static final String EXTRA_TAG = "android.nfc.extra.TAG"; - field @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public static final int FLAG_LISTEN_DISABLE = 0; // 0x0 - field @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public static final int FLAG_LISTEN_KEEP = -2147483648; // 0x80000000 - field @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public static final int FLAG_LISTEN_NFC_PASSIVE_A = 1; // 0x1 - field @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public static final int FLAG_LISTEN_NFC_PASSIVE_B = 2; // 0x2 - field @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public static final int FLAG_LISTEN_NFC_PASSIVE_F = 4; // 0x4 - field @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public static final int FLAG_READER_DISABLE = 0; // 0x0 - field @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public static final int FLAG_READER_KEEP = -2147483648; // 0x80000000 - field public static final int FLAG_READER_NFC_A = 1; // 0x1 - field public static final int FLAG_READER_NFC_B = 2; // 0x2 - field public static final int FLAG_READER_NFC_BARCODE = 16; // 0x10 - field public static final int FLAG_READER_NFC_F = 4; // 0x4 - field public static final int FLAG_READER_NFC_V = 8; // 0x8 - field public static final int FLAG_READER_NO_PLATFORM_SOUNDS = 256; // 0x100 - field public static final int FLAG_READER_SKIP_NDEF_CHECK = 128; // 0x80 - field public static final int PREFERRED_PAYMENT_CHANGED = 2; // 0x2 - field public static final int PREFERRED_PAYMENT_LOADED = 1; // 0x1 - field public static final int PREFERRED_PAYMENT_UPDATED = 3; // 0x3 - field public static final int STATE_OFF = 1; // 0x1 - field public static final int STATE_ON = 3; // 0x3 - field public static final int STATE_TURNING_OFF = 4; // 0x4 - field public static final int STATE_TURNING_ON = 2; // 0x2 - } - - @Deprecated public static interface NfcAdapter.CreateBeamUrisCallback { - method @Deprecated public android.net.Uri[] createBeamUris(android.nfc.NfcEvent); - } - - @Deprecated public static interface NfcAdapter.CreateNdefMessageCallback { - method @Deprecated public android.nfc.NdefMessage createNdefMessage(android.nfc.NfcEvent); - } - - @Deprecated public static interface NfcAdapter.OnNdefPushCompleteCallback { - method @Deprecated public void onNdefPushComplete(android.nfc.NfcEvent); - } - - public static interface NfcAdapter.OnTagRemovedListener { - method public void onTagRemoved(); - } - - public static interface NfcAdapter.ReaderCallback { - method public void onTagDiscovered(android.nfc.Tag); - } - - public final class NfcAntennaInfo implements android.os.Parcelable { - ctor public NfcAntennaInfo(int, int, boolean, @NonNull java.util.List<android.nfc.AvailableNfcAntenna>); - method public int describeContents(); - method @NonNull public java.util.List<android.nfc.AvailableNfcAntenna> getAvailableNfcAntennas(); - method public int getDeviceHeight(); - method public int getDeviceWidth(); - method public boolean isDeviceFoldable(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.nfc.NfcAntennaInfo> CREATOR; - } - - public final class NfcEvent { - field public final android.nfc.NfcAdapter nfcAdapter; - field public final int peerLlcpMajorVersion; - field public final int peerLlcpMinorVersion; - } - - public final class NfcManager { - method public android.nfc.NfcAdapter getDefaultAdapter(); - } - - public final class Tag implements android.os.Parcelable { - method public int describeContents(); - method public byte[] getId(); - method public String[] getTechList(); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.nfc.Tag> CREATOR; - } - - public class TagLostException extends java.io.IOException { - ctor public TagLostException(); - ctor public TagLostException(String); - } - - @FlaggedApi("android.nfc.enable_nfc_charging") public final class WlcListenerDeviceInfo implements android.os.Parcelable { - ctor public WlcListenerDeviceInfo(int, double, double, int); - method public int describeContents(); - method @FloatRange(from=0.0, to=100.0) public double getBatteryLevel(); - method public int getProductId(); - method public int getState(); - method public double getTemperature(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.nfc.WlcListenerDeviceInfo> CREATOR; - field public static final int STATE_CONNECTED_CHARGING = 2; // 0x2 - field public static final int STATE_CONNECTED_DISCHARGING = 3; // 0x3 - field public static final int STATE_DISCONNECTED = 1; // 0x1 - } - -} - -package android.nfc.cardemulation { - - public final class CardEmulation { - method public boolean categoryAllowsForegroundPreference(String); - method @Nullable @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public java.util.List<java.lang.String> getAidsForPreferredPaymentService(); - method public java.util.List<java.lang.String> getAidsForService(android.content.ComponentName, String); - method @FlaggedApi("android.nfc.enable_card_emulation_euicc") public int getDefaultNfcSubscriptionId(); - method @Nullable @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public CharSequence getDescriptionForPreferredPaymentService(); - method public static android.nfc.cardemulation.CardEmulation getInstance(android.nfc.NfcAdapter); - method @Nullable @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public String getRouteDestinationForPreferredPaymentService(); - method public int getSelectionModeForCategory(String); - method public boolean isDefaultServiceForAid(android.content.ComponentName, String); - method public boolean isDefaultServiceForCategory(android.content.ComponentName, String); - method @FlaggedApi("android.nfc.enable_card_emulation_euicc") public boolean isEuiccSupported(); - method public boolean registerAidsForService(android.content.ComponentName, String, java.util.List<java.lang.String>); - method @FlaggedApi("android.nfc.nfc_event_listener") public void registerNfcEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.nfc.cardemulation.CardEmulation.NfcEventCallback); - method @FlaggedApi("android.nfc.nfc_read_polling_loop") public boolean registerPollingLoopFilterForService(@NonNull android.content.ComponentName, @NonNull String, boolean); - method @FlaggedApi("android.nfc.nfc_read_polling_loop") public boolean registerPollingLoopPatternFilterForService(@NonNull android.content.ComponentName, @NonNull String, boolean); - method public boolean removeAidsForService(android.content.ComponentName, String); - method @FlaggedApi("android.nfc.nfc_read_polling_loop") public boolean removePollingLoopFilterForService(@NonNull android.content.ComponentName, @NonNull String); - method @FlaggedApi("android.nfc.nfc_read_polling_loop") public boolean removePollingLoopPatternFilterForService(@NonNull android.content.ComponentName, @NonNull String); - method @NonNull @RequiresPermission(android.Manifest.permission.NFC) public boolean setOffHostForService(@NonNull android.content.ComponentName, @NonNull String); - method public boolean setPreferredService(android.app.Activity, android.content.ComponentName); - method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean setShouldDefaultToObserveModeForService(@NonNull android.content.ComponentName, boolean); - method public boolean supportsAidPrefixRegistration(); - method @FlaggedApi("android.nfc.nfc_event_listener") public void unregisterNfcEventCallback(@NonNull android.nfc.cardemulation.CardEmulation.NfcEventCallback); - method @NonNull @RequiresPermission(android.Manifest.permission.NFC) public boolean unsetOffHostForService(@NonNull android.content.ComponentName); - method public boolean unsetPreferredService(android.app.Activity); - field @Deprecated public static final String ACTION_CHANGE_DEFAULT = "android.nfc.cardemulation.action.ACTION_CHANGE_DEFAULT"; - field public static final String CATEGORY_OTHER = "other"; - field public static final String CATEGORY_PAYMENT = "payment"; - field public static final String EXTRA_CATEGORY = "category"; - field public static final String EXTRA_SERVICE_COMPONENT = "component"; - field @FlaggedApi("android.nfc.nfc_event_listener") public static final int NFC_INTERNAL_ERROR_COMMAND_TIMEOUT = 3; // 0x3 - field @FlaggedApi("android.nfc.nfc_event_listener") public static final int NFC_INTERNAL_ERROR_NFC_CRASH_RESTART = 1; // 0x1 - field @FlaggedApi("android.nfc.nfc_event_listener") public static final int NFC_INTERNAL_ERROR_NFC_HARDWARE_ERROR = 2; // 0x2 - field @FlaggedApi("android.nfc.nfc_event_listener") public static final int NFC_INTERNAL_ERROR_UNKNOWN = 0; // 0x0 - field @FlaggedApi("android.nfc.nfc_associated_role_services") public static final String PROPERTY_ALLOW_SHARED_ROLE_PRIORITY = "android.nfc.cardemulation.PROPERTY_ALLOW_SHARED_ROLE_PRIORITY"; - field @FlaggedApi("android.nfc.nfc_override_recover_routing_table") public static final int PROTOCOL_AND_TECHNOLOGY_ROUTE_DEFAULT = 3; // 0x3 - field @FlaggedApi("android.nfc.nfc_override_recover_routing_table") public static final int PROTOCOL_AND_TECHNOLOGY_ROUTE_DH = 0; // 0x0 - field @FlaggedApi("android.nfc.nfc_override_recover_routing_table") public static final int PROTOCOL_AND_TECHNOLOGY_ROUTE_ESE = 1; // 0x1 - field @FlaggedApi("android.nfc.nfc_override_recover_routing_table") public static final int PROTOCOL_AND_TECHNOLOGY_ROUTE_UICC = 2; // 0x2 - field @FlaggedApi("android.nfc.nfc_override_recover_routing_table") public static final int PROTOCOL_AND_TECHNOLOGY_ROUTE_UNSET = -1; // 0xffffffff - field public static final int SELECTION_MODE_ALWAYS_ASK = 1; // 0x1 - field public static final int SELECTION_MODE_ASK_IF_CONFLICT = 2; // 0x2 - field public static final int SELECTION_MODE_PREFER_DEFAULT = 0; // 0x0 - } - - @FlaggedApi("android.nfc.nfc_event_listener") public static interface CardEmulation.NfcEventCallback { - method @FlaggedApi("android.nfc.nfc_event_listener") public default void onAidConflictOccurred(@NonNull String); - method @FlaggedApi("android.nfc.nfc_event_listener") public default void onAidNotRouted(@NonNull String); - method @FlaggedApi("android.nfc.nfc_event_listener") public default void onInternalErrorReported(int); - method @FlaggedApi("android.nfc.nfc_event_listener") public default void onNfcStateChanged(int); - method @FlaggedApi("android.nfc.nfc_event_listener") public default void onObserveModeStateChanged(boolean); - method @FlaggedApi("android.nfc.nfc_event_listener") public default void onPreferredServiceChanged(boolean); - method @FlaggedApi("android.nfc.nfc_event_listener") public default void onRemoteFieldChanged(boolean); - } - - public abstract class HostApduService extends android.app.Service { - ctor public HostApduService(); - method public final void notifyUnhandled(); - 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.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 public static final String SERVICE_INTERFACE = "android.nfc.cardemulation.action.HOST_APDU_SERVICE"; - field public static final String SERVICE_META_DATA = "android.nfc.cardemulation.host_apdu_service"; - } - - public abstract class HostNfcFService extends android.app.Service { - ctor public HostNfcFService(); - method public final android.os.IBinder onBind(android.content.Intent); - method public abstract void onDeactivated(int); - method public abstract byte[] processNfcFPacket(byte[], android.os.Bundle); - method public final void sendResponsePacket(byte[]); - field public static final int DEACTIVATION_LINK_LOSS = 0; // 0x0 - field public static final String SERVICE_INTERFACE = "android.nfc.cardemulation.action.HOST_NFCF_SERVICE"; - field public static final String SERVICE_META_DATA = "android.nfc.cardemulation.host_nfcf_service"; - } - - public final class NfcFCardEmulation { - method public boolean disableService(android.app.Activity) throws java.lang.RuntimeException; - method public boolean enableService(android.app.Activity, android.content.ComponentName) throws java.lang.RuntimeException; - method public static android.nfc.cardemulation.NfcFCardEmulation getInstance(android.nfc.NfcAdapter); - method public String getNfcid2ForService(android.content.ComponentName) throws java.lang.RuntimeException; - method public String getSystemCodeForService(android.content.ComponentName) throws java.lang.RuntimeException; - method public boolean registerSystemCodeForService(android.content.ComponentName, String) throws java.lang.RuntimeException; - method public boolean setNfcid2ForService(android.content.ComponentName, String) throws java.lang.RuntimeException; - method public boolean unregisterSystemCodeForService(android.content.ComponentName) throws java.lang.RuntimeException; - } - - public abstract class OffHostApduService extends android.app.Service { - ctor public OffHostApduService(); - field public static final String SERVICE_INTERFACE = "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE"; - 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 { - method public int describeContents(); - method @NonNull public byte[] getData(); - method public long getTimestamp(); - method public boolean getTriggeredAutoTransact(); - method public int getType(); - method public int getVendorSpecificGain(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.nfc.cardemulation.PollingFrame> CREATOR; - field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final int POLLING_LOOP_TYPE_A = 65; // 0x41 - field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final int POLLING_LOOP_TYPE_B = 66; // 0x42 - field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final int POLLING_LOOP_TYPE_F = 70; // 0x46 - field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final int POLLING_LOOP_TYPE_OFF = 88; // 0x58 - field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final int POLLING_LOOP_TYPE_ON = 79; // 0x4f - field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final int POLLING_LOOP_TYPE_UNKNOWN = 85; // 0x55 - } - -} - -package android.nfc.tech { - - public final class IsoDep implements android.nfc.tech.TagTechnology { - method public void close() throws java.io.IOException; - method public void connect() throws java.io.IOException; - method public static android.nfc.tech.IsoDep get(android.nfc.Tag); - method public byte[] getHiLayerResponse(); - method public byte[] getHistoricalBytes(); - method public int getMaxTransceiveLength(); - method public android.nfc.Tag getTag(); - method public int getTimeout(); - method public boolean isConnected(); - method public boolean isExtendedLengthApduSupported(); - method public void setTimeout(int); - method public byte[] transceive(byte[]) throws java.io.IOException; - } - - public final class MifareClassic implements android.nfc.tech.TagTechnology { - method public boolean authenticateSectorWithKeyA(int, byte[]) throws java.io.IOException; - method public boolean authenticateSectorWithKeyB(int, byte[]) throws java.io.IOException; - method public int blockToSector(int); - method public void close() throws java.io.IOException; - method public void connect() throws java.io.IOException; - method public void decrement(int, int) throws java.io.IOException; - method public static android.nfc.tech.MifareClassic get(android.nfc.Tag); - method public int getBlockCount(); - method public int getBlockCountInSector(int); - method public int getMaxTransceiveLength(); - method public int getSectorCount(); - method public int getSize(); - method public android.nfc.Tag getTag(); - method public int getTimeout(); - method public int getType(); - method public void increment(int, int) throws java.io.IOException; - method public boolean isConnected(); - method public byte[] readBlock(int) throws java.io.IOException; - method public void restore(int) throws java.io.IOException; - method public int sectorToBlock(int); - method public void setTimeout(int); - method public byte[] transceive(byte[]) throws java.io.IOException; - method public void transfer(int) throws java.io.IOException; - method public void writeBlock(int, byte[]) throws java.io.IOException; - field public static final int BLOCK_SIZE = 16; // 0x10 - field public static final byte[] KEY_DEFAULT; - field public static final byte[] KEY_MIFARE_APPLICATION_DIRECTORY; - field public static final byte[] KEY_NFC_FORUM; - field public static final int SIZE_1K = 1024; // 0x400 - field public static final int SIZE_2K = 2048; // 0x800 - field public static final int SIZE_4K = 4096; // 0x1000 - field public static final int SIZE_MINI = 320; // 0x140 - field public static final int TYPE_CLASSIC = 0; // 0x0 - field public static final int TYPE_PLUS = 1; // 0x1 - field public static final int TYPE_PRO = 2; // 0x2 - field public static final int TYPE_UNKNOWN = -1; // 0xffffffff - } - - public final class MifareUltralight implements android.nfc.tech.TagTechnology { - method public void close() throws java.io.IOException; - method public void connect() throws java.io.IOException; - method public static android.nfc.tech.MifareUltralight get(android.nfc.Tag); - method public int getMaxTransceiveLength(); - method public android.nfc.Tag getTag(); - method public int getTimeout(); - method public int getType(); - method public boolean isConnected(); - method public byte[] readPages(int) throws java.io.IOException; - method public void setTimeout(int); - method public byte[] transceive(byte[]) throws java.io.IOException; - method public void writePage(int, byte[]) throws java.io.IOException; - field public static final int PAGE_SIZE = 4; // 0x4 - field public static final int TYPE_ULTRALIGHT = 1; // 0x1 - field public static final int TYPE_ULTRALIGHT_C = 2; // 0x2 - field public static final int TYPE_UNKNOWN = -1; // 0xffffffff - } - - public final class Ndef implements android.nfc.tech.TagTechnology { - method public boolean canMakeReadOnly(); - method public void close() throws java.io.IOException; - method public void connect() throws java.io.IOException; - method public static android.nfc.tech.Ndef get(android.nfc.Tag); - method public android.nfc.NdefMessage getCachedNdefMessage(); - method public int getMaxSize(); - method public android.nfc.NdefMessage getNdefMessage() throws android.nfc.FormatException, java.io.IOException; - method public android.nfc.Tag getTag(); - method public String getType(); - method public boolean isConnected(); - method public boolean isWritable(); - method public boolean makeReadOnly() throws java.io.IOException; - method public void writeNdefMessage(android.nfc.NdefMessage) throws android.nfc.FormatException, java.io.IOException; - field public static final String MIFARE_CLASSIC = "com.nxp.ndef.mifareclassic"; - field public static final String NFC_FORUM_TYPE_1 = "org.nfcforum.ndef.type1"; - field public static final String NFC_FORUM_TYPE_2 = "org.nfcforum.ndef.type2"; - field public static final String NFC_FORUM_TYPE_3 = "org.nfcforum.ndef.type3"; - field public static final String NFC_FORUM_TYPE_4 = "org.nfcforum.ndef.type4"; - } - - public final class NdefFormatable implements android.nfc.tech.TagTechnology { - method public void close() throws java.io.IOException; - method public void connect() throws java.io.IOException; - method public void format(android.nfc.NdefMessage) throws android.nfc.FormatException, java.io.IOException; - method public void formatReadOnly(android.nfc.NdefMessage) throws android.nfc.FormatException, java.io.IOException; - method public static android.nfc.tech.NdefFormatable get(android.nfc.Tag); - method public android.nfc.Tag getTag(); - method public boolean isConnected(); - } - - public final class NfcA implements android.nfc.tech.TagTechnology { - method public void close() throws java.io.IOException; - method public void connect() throws java.io.IOException; - method public static android.nfc.tech.NfcA get(android.nfc.Tag); - method public byte[] getAtqa(); - method public int getMaxTransceiveLength(); - method public short getSak(); - method public android.nfc.Tag getTag(); - method public int getTimeout(); - method public boolean isConnected(); - method public void setTimeout(int); - method public byte[] transceive(byte[]) throws java.io.IOException; - } - - public final class NfcB implements android.nfc.tech.TagTechnology { - method public void close() throws java.io.IOException; - method public void connect() throws java.io.IOException; - method public static android.nfc.tech.NfcB get(android.nfc.Tag); - method public byte[] getApplicationData(); - method public int getMaxTransceiveLength(); - method public byte[] getProtocolInfo(); - method public android.nfc.Tag getTag(); - method public boolean isConnected(); - method public byte[] transceive(byte[]) throws java.io.IOException; - } - - public final class NfcBarcode implements android.nfc.tech.TagTechnology { - method public void close() throws java.io.IOException; - method public void connect() throws java.io.IOException; - method public static android.nfc.tech.NfcBarcode get(android.nfc.Tag); - method public byte[] getBarcode(); - method public android.nfc.Tag getTag(); - method public int getType(); - method public boolean isConnected(); - field public static final int TYPE_KOVIO = 1; // 0x1 - field public static final int TYPE_UNKNOWN = -1; // 0xffffffff - } - - public final class NfcF implements android.nfc.tech.TagTechnology { - method public void close() throws java.io.IOException; - method public void connect() throws java.io.IOException; - method public static android.nfc.tech.NfcF get(android.nfc.Tag); - method public byte[] getManufacturer(); - method public int getMaxTransceiveLength(); - method public byte[] getSystemCode(); - method public android.nfc.Tag getTag(); - method public int getTimeout(); - method public boolean isConnected(); - method public void setTimeout(int); - method public byte[] transceive(byte[]) throws java.io.IOException; - } - - public final class NfcV implements android.nfc.tech.TagTechnology { - method public void close() throws java.io.IOException; - method public void connect() throws java.io.IOException; - method public static android.nfc.tech.NfcV get(android.nfc.Tag); - method public byte getDsfId(); - method public int getMaxTransceiveLength(); - method public byte getResponseFlags(); - method public android.nfc.Tag getTag(); - method public boolean isConnected(); - method public byte[] transceive(byte[]) throws java.io.IOException; - } - - public interface TagTechnology extends java.io.Closeable { - method public void connect() throws java.io.IOException; - method public android.nfc.Tag getTag(); - method public boolean isConnected(); - } - -} - diff --git a/nfc/api/lint-baseline.txt b/nfc/api/lint-baseline.txt deleted file mode 100644 index ef9aab6e7641..000000000000 --- a/nfc/api/lint-baseline.txt +++ /dev/null @@ -1,95 +0,0 @@ -// Baseline format: 1.0 -BroadcastBehavior: android.nfc.NfcAdapter#ACTION_ADAPTER_STATE_CHANGED: - Field 'ACTION_ADAPTER_STATE_CHANGED' is missing @BroadcastBehavior -BroadcastBehavior: android.nfc.NfcAdapter#ACTION_PREFERRED_PAYMENT_CHANGED: - Field 'ACTION_PREFERRED_PAYMENT_CHANGED' is missing @BroadcastBehavior -BroadcastBehavior: android.nfc.NfcAdapter#ACTION_TRANSACTION_DETECTED: - Field 'ACTION_TRANSACTION_DETECTED' is missing @BroadcastBehavior - - -MissingNullability: android.nfc.cardemulation.OffHostApduService#onBind(android.content.Intent): - Missing nullability on method `onBind` return -MissingNullability: android.nfc.cardemulation.OffHostApduService#onBind(android.content.Intent) parameter #0: - Missing nullability on parameter `intent` in method `onBind` - - -RequiresPermission: android.nfc.NfcAdapter#disableForegroundDispatch(android.app.Activity): - Method 'disableForegroundDispatch' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.NfcAdapter#enableForegroundDispatch(android.app.Activity, android.app.PendingIntent, android.content.IntentFilter[], String[][]): - Method 'enableForegroundDispatch' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.cardemulation.CardEmulation#isDefaultServiceForAid(android.content.ComponentName, String): - Method 'isDefaultServiceForAid' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.cardemulation.CardEmulation#isDefaultServiceForCategory(android.content.ComponentName, String): - Method 'isDefaultServiceForCategory' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.cardemulation.CardEmulation#setOffHostForService(android.content.ComponentName, String): - Method 'setOffHostForService' documentation mentions permissions already declared by @RequiresPermission -RequiresPermission: android.nfc.tech.IsoDep#getTimeout(): - Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.IsoDep#setTimeout(int): - Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.IsoDep#transceive(byte[]): - Method 'transceive' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#authenticateSectorWithKeyA(int, byte[]): - Method 'authenticateSectorWithKeyA' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#authenticateSectorWithKeyB(int, byte[]): - Method 'authenticateSectorWithKeyB' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#decrement(int, int): - Method 'decrement' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#getTimeout(): - Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#increment(int, int): - Method 'increment' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#readBlock(int): - Method 'readBlock' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#restore(int): - Method 'restore' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#setTimeout(int): - Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#transceive(byte[]): - Method 'transceive' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#transfer(int): - Method 'transfer' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#writeBlock(int, byte[]): - Method 'writeBlock' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareUltralight#getTimeout(): - Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareUltralight#readPages(int): - Method 'readPages' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareUltralight#setTimeout(int): - Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareUltralight#transceive(byte[]): - Method 'transceive' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareUltralight#writePage(int, byte[]): - Method 'writePage' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.Ndef#getNdefMessage(): - Method 'getNdefMessage' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.Ndef#isWritable(): - Method 'isWritable' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.Ndef#makeReadOnly(): - Method 'makeReadOnly' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.Ndef#writeNdefMessage(android.nfc.NdefMessage): - Method 'writeNdefMessage' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NdefFormatable#format(android.nfc.NdefMessage): - Method 'format' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NdefFormatable#formatReadOnly(android.nfc.NdefMessage): - Method 'formatReadOnly' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NfcA#getTimeout(): - Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NfcA#setTimeout(int): - Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NfcA#transceive(byte[]): - Method 'transceive' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NfcB#transceive(byte[]): - Method 'transceive' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NfcF#getTimeout(): - Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NfcF#setTimeout(int): - Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NfcF#transceive(byte[]): - Method 'transceive' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NfcV#transceive(byte[]): - Method 'transceive' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.TagTechnology#close(): - Method 'close' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.TagTechnology#connect(): - Method 'connect' documentation mentions permissions without declaring @RequiresPermission diff --git a/nfc/api/module-lib-current.txt b/nfc/api/module-lib-current.txt deleted file mode 100644 index 5ebe91111ec0..000000000000 --- a/nfc/api/module-lib-current.txt +++ /dev/null @@ -1,10 +0,0 @@ -// Signature format: 2.0 -package android.nfc { - - public class NfcFrameworkInitializer { - method public static void registerServiceWrappers(); - method public static void setNfcServiceManager(@NonNull android.nfc.NfcServiceManager); - } - -} - diff --git a/nfc/api/module-lib-lint-baseline.txt b/nfc/api/module-lib-lint-baseline.txt deleted file mode 100644 index f7f8ee3ddda5..000000000000 --- a/nfc/api/module-lib-lint-baseline.txt +++ /dev/null @@ -1,93 +0,0 @@ -// Baseline format: 1.0 -BroadcastBehavior: android.nfc.NfcAdapter#ACTION_ADAPTER_STATE_CHANGED: - Field 'ACTION_ADAPTER_STATE_CHANGED' is missing @BroadcastBehavior -BroadcastBehavior: android.nfc.NfcAdapter#ACTION_PREFERRED_PAYMENT_CHANGED: - Field 'ACTION_PREFERRED_PAYMENT_CHANGED' is missing @BroadcastBehavior -BroadcastBehavior: android.nfc.NfcAdapter#ACTION_REQUIRE_UNLOCK_FOR_NFC: - Field 'ACTION_REQUIRE_UNLOCK_FOR_NFC' is missing @BroadcastBehavior -BroadcastBehavior: android.nfc.NfcAdapter#ACTION_TRANSACTION_DETECTED: - Field 'ACTION_TRANSACTION_DETECTED' is missing @BroadcastBehavior - -RequiresPermission: android.nfc.NfcAdapter#disableForegroundDispatch(android.app.Activity): - Method 'disableForegroundDispatch' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.NfcAdapter#enableForegroundDispatch(android.app.Activity, android.app.PendingIntent, android.content.IntentFilter[], String[][]): - Method 'enableForegroundDispatch' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.cardemulation.CardEmulation#isDefaultServiceForAid(android.content.ComponentName, String): - Method 'isDefaultServiceForAid' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.cardemulation.CardEmulation#isDefaultServiceForCategory(android.content.ComponentName, String): - Method 'isDefaultServiceForCategory' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.cardemulation.CardEmulation#setOffHostForService(android.content.ComponentName, String): - Method 'setOffHostForService' documentation mentions permissions already declared by @RequiresPermission -RequiresPermission: android.nfc.tech.IsoDep#getTimeout(): - Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.IsoDep#setTimeout(int): - Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.IsoDep#transceive(byte[]): - Method 'transceive' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#authenticateSectorWithKeyA(int, byte[]): - Method 'authenticateSectorWithKeyA' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#authenticateSectorWithKeyB(int, byte[]): - Method 'authenticateSectorWithKeyB' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#decrement(int, int): - Method 'decrement' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#getTimeout(): - Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#increment(int, int): - Method 'increment' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#readBlock(int): - Method 'readBlock' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#restore(int): - Method 'restore' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#setTimeout(int): - Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#transceive(byte[]): - Method 'transceive' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#transfer(int): - Method 'transfer' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#writeBlock(int, byte[]): - Method 'writeBlock' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareUltralight#getTimeout(): - Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareUltralight#readPages(int): - Method 'readPages' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareUltralight#setTimeout(int): - Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareUltralight#transceive(byte[]): - Method 'transceive' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareUltralight#writePage(int, byte[]): - Method 'writePage' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.Ndef#getNdefMessage(): - Method 'getNdefMessage' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.Ndef#isWritable(): - Method 'isWritable' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.Ndef#makeReadOnly(): - Method 'makeReadOnly' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.Ndef#writeNdefMessage(android.nfc.NdefMessage): - Method 'writeNdefMessage' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NdefFormatable#format(android.nfc.NdefMessage): - Method 'format' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NdefFormatable#formatReadOnly(android.nfc.NdefMessage): - Method 'formatReadOnly' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NfcA#getTimeout(): - Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NfcA#setTimeout(int): - Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NfcA#transceive(byte[]): - Method 'transceive' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NfcB#transceive(byte[]): - Method 'transceive' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NfcF#getTimeout(): - Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NfcF#setTimeout(int): - Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NfcF#transceive(byte[]): - Method 'transceive' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NfcV#transceive(byte[]): - Method 'transceive' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.TagTechnology#close(): - Method 'close' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.TagTechnology#connect(): - Method 'connect' documentation mentions permissions without declaring @RequiresPermission - -SdkConstant: android.nfc.NfcAdapter#ACTION_REQUIRE_UNLOCK_FOR_NFC: - Field 'ACTION_REQUIRE_UNLOCK_FOR_NFC' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) diff --git a/nfc/api/module-lib-removed.txt b/nfc/api/module-lib-removed.txt deleted file mode 100644 index d802177e249b..000000000000 --- a/nfc/api/module-lib-removed.txt +++ /dev/null @@ -1 +0,0 @@ -// Signature format: 2.0 diff --git a/nfc/api/removed.txt b/nfc/api/removed.txt deleted file mode 100644 index fb82b5ddbb21..000000000000 --- a/nfc/api/removed.txt +++ /dev/null @@ -1,17 +0,0 @@ -// Signature format: 2.0 -package android.nfc { - - public final class NfcAdapter { - method @Deprecated @android.compat.annotation.UnsupportedAppUsage public void disableForegroundNdefPush(android.app.Activity); - method @Deprecated @android.compat.annotation.UnsupportedAppUsage public void enableForegroundNdefPush(android.app.Activity, android.nfc.NdefMessage); - method @Deprecated @android.compat.annotation.UnsupportedAppUsage public boolean invokeBeam(android.app.Activity); - method @Deprecated @android.compat.annotation.UnsupportedAppUsage public boolean isNdefPushEnabled(); - method @Deprecated @android.compat.annotation.UnsupportedAppUsage public void setBeamPushUris(android.net.Uri[], android.app.Activity); - method @Deprecated @android.compat.annotation.UnsupportedAppUsage public void setBeamPushUrisCallback(android.nfc.NfcAdapter.CreateBeamUrisCallback, android.app.Activity); - method @Deprecated @android.compat.annotation.UnsupportedAppUsage public void setNdefPushMessage(android.nfc.NdefMessage, android.app.Activity, android.app.Activity...); - method @Deprecated @android.compat.annotation.UnsupportedAppUsage public void setNdefPushMessageCallback(android.nfc.NfcAdapter.CreateNdefMessageCallback, android.app.Activity, android.app.Activity...); - method @Deprecated @android.compat.annotation.UnsupportedAppUsage public void setOnNdefPushCompleteCallback(android.nfc.NfcAdapter.OnNdefPushCompleteCallback, android.app.Activity, android.app.Activity...); - } - -} - diff --git a/nfc/api/system-current.txt b/nfc/api/system-current.txt deleted file mode 100644 index 6e69da1c6daa..000000000000 --- a/nfc/api/system-current.txt +++ /dev/null @@ -1,256 +0,0 @@ -// Signature format: 2.0 -package android.nfc { - - public final class NfcAdapter { - method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean addNfcUnlockHandler(android.nfc.NfcAdapter.NfcUnlockHandler, String[]); - method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean disable(boolean); - method @FlaggedApi("android.nfc.enable_nfc_reader_option") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableReaderOption(boolean); - method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableSecureNfc(boolean); - method @FlaggedApi("android.nfc.enable_nfc_mainline") public int getAdapterState(); - method @FlaggedApi("android.nfc.nfc_oem_extension") @NonNull public android.nfc.NfcOemExtension getNfcOemExtension(); - method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public java.util.Map<java.lang.String,java.lang.Boolean> getTagIntentAppPreferenceForUser(int); - method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean isControllerAlwaysOn(); - method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean isControllerAlwaysOnSupported(); - method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public void registerControllerAlwaysOnListener(@NonNull java.util.concurrent.Executor, @NonNull android.nfc.NfcAdapter.ControllerAlwaysOnListener); - method @FlaggedApi("android.nfc.nfc_vendor_cmd") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void registerNfcVendorNciCallback(@NonNull java.util.concurrent.Executor, @NonNull android.nfc.NfcAdapter.NfcVendorNciCallback); - method @FlaggedApi("android.nfc.enable_nfc_charging") public void registerWlcStateListener(@NonNull java.util.concurrent.Executor, @NonNull android.nfc.NfcAdapter.WlcStateListener); - method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean removeNfcUnlockHandler(android.nfc.NfcAdapter.NfcUnlockHandler); - method @FlaggedApi("android.nfc.nfc_vendor_cmd") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public int sendVendorNciMessage(int, @IntRange(from=0, to=15) int, @IntRange(from=0) int, @NonNull byte[]); - method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean setControllerAlwaysOn(boolean); - method @FlaggedApi("android.nfc.enable_nfc_mainline") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setReaderModePollingEnabled(boolean); - method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public int setTagIntentAppPreferenceForUser(int, @NonNull String, boolean); - method @FlaggedApi("android.nfc.enable_nfc_charging") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean setWlcEnabled(boolean); - method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public void unregisterControllerAlwaysOnListener(@NonNull android.nfc.NfcAdapter.ControllerAlwaysOnListener); - method @FlaggedApi("android.nfc.nfc_vendor_cmd") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void unregisterNfcVendorNciCallback(@NonNull android.nfc.NfcAdapter.NfcVendorNciCallback); - method @FlaggedApi("android.nfc.enable_nfc_charging") public void unregisterWlcStateListener(@NonNull android.nfc.NfcAdapter.WlcStateListener); - field @FlaggedApi("android.nfc.enable_nfc_mainline") public static final String ACTION_REQUIRE_UNLOCK_FOR_NFC = "android.nfc.action.REQUIRE_UNLOCK_FOR_NFC"; - field @FlaggedApi("android.nfc.enable_nfc_mainline") @RequiresPermission(android.Manifest.permission.SHOW_CUSTOMIZED_RESOLVER) public static final String ACTION_SHOW_NFC_RESOLVER = "android.nfc.action.SHOW_NFC_RESOLVER"; - field @FlaggedApi("android.nfc.enable_nfc_mainline") public static final String EXTRA_RESOLVE_INFOS = "android.nfc.extra.RESOLVE_INFOS"; - field @FlaggedApi("android.nfc.nfc_set_default_disc_tech") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public static final int FLAG_SET_DEFAULT_TECH = 1073741824; // 0x40000000 - field @FlaggedApi("android.nfc.nfc_vendor_cmd") public static final int MESSAGE_TYPE_COMMAND = 1; // 0x1 - field @FlaggedApi("android.nfc.nfc_vendor_cmd") public static final int SEND_VENDOR_NCI_STATUS_FAILED = 3; // 0x3 - field @FlaggedApi("android.nfc.nfc_vendor_cmd") public static final int SEND_VENDOR_NCI_STATUS_MESSAGE_CORRUPTED = 2; // 0x2 - field @FlaggedApi("android.nfc.nfc_vendor_cmd") public static final int SEND_VENDOR_NCI_STATUS_REJECTED = 1; // 0x1 - field @FlaggedApi("android.nfc.nfc_vendor_cmd") public static final int SEND_VENDOR_NCI_STATUS_SUCCESS = 0; // 0x0 - field public static final int TAG_INTENT_APP_PREF_RESULT_PACKAGE_NOT_FOUND = -1; // 0xffffffff - field public static final int TAG_INTENT_APP_PREF_RESULT_SUCCESS = 0; // 0x0 - field public static final int TAG_INTENT_APP_PREF_RESULT_UNAVAILABLE = -2; // 0xfffffffe - } - - public static interface NfcAdapter.ControllerAlwaysOnListener { - method public void onControllerAlwaysOnChanged(boolean); - } - - public static interface NfcAdapter.NfcUnlockHandler { - method public boolean onUnlockAttempted(android.nfc.Tag); - } - - @FlaggedApi("android.nfc.nfc_vendor_cmd") public static interface NfcAdapter.NfcVendorNciCallback { - method @FlaggedApi("android.nfc.nfc_vendor_cmd") public void onVendorNciNotification(@IntRange(from=9, to=15) int, int, @NonNull byte[]); - method @FlaggedApi("android.nfc.nfc_vendor_cmd") public void onVendorNciResponse(@IntRange(from=0, to=15) int, int, @NonNull byte[]); - } - - @FlaggedApi("android.nfc.enable_nfc_charging") public static interface NfcAdapter.WlcStateListener { - method public void onWlcStateChanged(@NonNull android.nfc.WlcListenerDeviceInfo); - } - - @FlaggedApi("android.nfc.nfc_oem_extension") public final class NfcOemExtension { - method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void clearPreference(); - method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public int forceRoutingTableCommit(); - method @FlaggedApi("android.nfc.nfc_oem_extension") @NonNull public java.util.Map<java.lang.String,java.lang.Integer> getActiveNfceeList(); - method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public long getMaxPausePollingTimeoutMills(); - method @FlaggedApi("android.nfc.nfc_oem_extension") @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public android.nfc.RoutingStatus getRoutingStatus(); - method @FlaggedApi("android.nfc.nfc_oem_extension") @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public java.util.List<android.nfc.NfcRoutingTableEntry> getRoutingTable(); - method @FlaggedApi("android.nfc.nfc_oem_extension") @NonNull public android.nfc.T4tNdefNfcee getT4tNdefNfcee(); - method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean hasUserEnabledNfc(); - method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean isAutoChangeEnabled(); - method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean isTagPresent(); - method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void maybeTriggerFirmwareUpdate(); - method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void overwriteRoutingTable(int, int, int, int); - method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public int pausePolling(long); - method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.nfc.NfcOemExtension.Callback); - method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public int resumePolling(); - method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setAutoChangeEnabled(boolean); - method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public void setControllerAlwaysOnMode(int); - method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void synchronizeScreenState(); - method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void triggerInitialization(); - method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void unregisterCallback(@NonNull android.nfc.NfcOemExtension.Callback); - field public static final int COMMIT_ROUTING_STATUS_FAILED = 3; // 0x3 - field public static final int COMMIT_ROUTING_STATUS_FAILED_UPDATE_IN_PROGRESS = 6; // 0x6 - field public static final int COMMIT_ROUTING_STATUS_OK = 0; // 0x0 - field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int DISABLE = 0; // 0x0 - field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int ENABLE_DEFAULT = 1; // 0x1 - field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int ENABLE_EE = 3; // 0x3 - field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int ENABLE_TRANSPARENT = 2; // 0x2 - field public static final int HCE_ACTIVATE = 1; // 0x1 - field public static final int HCE_DATA_TRANSFERRED = 2; // 0x2 - field public static final int HCE_DEACTIVATE = 3; // 0x3 - field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int NFCEE_TECH_A = 1; // 0x1 - field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int NFCEE_TECH_B = 2; // 0x2 - field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int NFCEE_TECH_F = 4; // 0x4 - field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int NFCEE_TECH_NONE = 0; // 0x0 - field public static final int POLLING_STATE_CHANGE_ALREADY_IN_REQUESTED_STATE = 2; // 0x2 - field public static final int POLLING_STATE_CHANGE_SUCCEEDED = 1; // 0x1 - field public static final int STATUS_OK = 0; // 0x0 - field public static final int STATUS_UNKNOWN_ERROR = 1; // 0x1 - } - - public static interface NfcOemExtension.Callback { - method public void onApplyRouting(@NonNull java.util.function.Consumer<java.lang.Boolean>); - method public void onBootFinished(int); - method public void onBootStarted(); - method public void onCardEmulationActivated(boolean); - method public void onDisableFinished(int); - method public void onDisableRequested(@NonNull java.util.function.Consumer<java.lang.Boolean>); - method public void onDisableStarted(); - method public void onEeListenActivated(boolean); - method public void onEeUpdated(); - method public void onEnableFinished(int); - method public void onEnableRequested(@NonNull java.util.function.Consumer<java.lang.Boolean>); - method public void onEnableStarted(); - method public void onExtractOemPackages(@NonNull android.nfc.NdefMessage, @NonNull java.util.function.Consumer<java.util.List<java.lang.String>>); - method public void onGetOemAppSearchIntent(@NonNull java.util.List<java.lang.String>, @NonNull java.util.function.Consumer<android.content.Intent>); - method public void onHceEventReceived(int); - method public void onLaunchHceAppChooserActivity(@NonNull String, @NonNull java.util.List<android.nfc.cardemulation.ApduServiceInfo>, @NonNull android.content.ComponentName, @NonNull String); - method public void onLaunchHceTapAgainDialog(@NonNull android.nfc.cardemulation.ApduServiceInfo, @NonNull String); - method public void onLogEventNotified(@NonNull android.nfc.OemLogItems); - method public void onNdefMessage(@NonNull android.nfc.Tag, @NonNull android.nfc.NdefMessage, @NonNull java.util.function.Consumer<java.lang.Boolean>); - method public void onNdefRead(@NonNull java.util.function.Consumer<java.lang.Boolean>); - method public void onReaderOptionChanged(boolean); - method public void onRfDiscoveryStarted(boolean); - method public void onRfFieldDetected(boolean); - method public void onRoutingChanged(@NonNull java.util.function.Consumer<java.lang.Boolean>); - method public void onRoutingTableFull(); - method public void onStateUpdated(int); - method public void onTagConnected(boolean); - method public void onTagDispatch(@NonNull java.util.function.Consumer<java.lang.Boolean>); - } - - @FlaggedApi("android.nfc.nfc_oem_extension") public abstract class NfcRoutingTableEntry { - method public int getNfceeId(); - method public int getRouteType(); - method public int getType(); - field public static final int TYPE_AID = 0; // 0x0 - field public static final int TYPE_PROTOCOL = 1; // 0x1 - field public static final int TYPE_SYSTEM_CODE = 3; // 0x3 - field public static final int TYPE_TECHNOLOGY = 2; // 0x2 - } - - @FlaggedApi("android.nfc.nfc_oem_extension") public final class OemLogItems implements android.os.Parcelable { - method public int describeContents(); - method public int getAction(); - method public int getCallingPid(); - method @Nullable public byte[] getCommandApdu(); - method public int getEvent(); - method @Nullable public byte[] getResponseApdu(); - method @Nullable public java.time.Instant getRfFieldEventTimeMillis(); - method @Nullable public android.nfc.Tag getTag(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.nfc.OemLogItems> CREATOR; - field public static final int EVENT_DISABLE = 2; // 0x2 - field public static final int EVENT_ENABLE = 1; // 0x1 - field public static final int EVENT_UNSET = 0; // 0x0 - field public static final int LOG_ACTION_HCE_DATA = 516; // 0x204 - field public static final int LOG_ACTION_NFC_TOGGLE = 513; // 0x201 - field public static final int LOG_ACTION_RF_FIELD_STATE_CHANGED = 1; // 0x1 - field public static final int LOG_ACTION_SCREEN_STATE_CHANGED = 518; // 0x206 - field public static final int LOG_ACTION_TAG_DETECTED = 3; // 0x3 - } - - @FlaggedApi("android.nfc.nfc_oem_extension") public class RoutingStatus { - method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public int getDefaultIsoDepRoute(); - method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public int getDefaultOffHostRoute(); - method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public int getDefaultRoute(); - } - - @FlaggedApi("android.nfc.nfc_oem_extension") public class RoutingTableAidEntry extends android.nfc.NfcRoutingTableEntry { - method @FlaggedApi("android.nfc.nfc_oem_extension") @NonNull public String getAid(); - } - - @FlaggedApi("android.nfc.nfc_oem_extension") public class RoutingTableProtocolEntry extends android.nfc.NfcRoutingTableEntry { - method @FlaggedApi("android.nfc.nfc_oem_extension") public int getProtocol(); - field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int PROTOCOL_ISO_DEP = 4; // 0x4 - field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int PROTOCOL_NDEF = 7; // 0x7 - field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int PROTOCOL_NFC_DEP = 5; // 0x5 - field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int PROTOCOL_T1T = 1; // 0x1 - field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int PROTOCOL_T2T = 2; // 0x2 - field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int PROTOCOL_T3T = 3; // 0x3 - field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int PROTOCOL_T5T = 6; // 0x6 - field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int PROTOCOL_UNDETERMINED = 0; // 0x0 - field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int PROTOCOL_UNSUPPORTED = -1; // 0xffffffff - } - - @FlaggedApi("android.nfc.nfc_oem_extension") public class RoutingTableSystemCodeEntry extends android.nfc.NfcRoutingTableEntry { - method @FlaggedApi("android.nfc.nfc_oem_extension") @NonNull public byte[] getSystemCode(); - } - - @FlaggedApi("android.nfc.nfc_oem_extension") public class RoutingTableTechnologyEntry extends android.nfc.NfcRoutingTableEntry { - method @FlaggedApi("android.nfc.nfc_oem_extension") public int getTechnology(); - field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int TECHNOLOGY_A = 0; // 0x0 - field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int TECHNOLOGY_B = 1; // 0x1 - field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int TECHNOLOGY_F = 2; // 0x2 - field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int TECHNOLOGY_UNSUPPORTED = -1; // 0xffffffff - field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int TECHNOLOGY_V = 3; // 0x3 - } - - @FlaggedApi("android.nfc.nfc_oem_extension") public final class T4tNdefNfcee { - method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) @WorkerThread public int clearData(); - method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean isOperationOngoing(); - method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean isSupported(); - method @Nullable @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) @WorkerThread public android.nfc.T4tNdefNfceeCcFileInfo readCcfile(); - method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) @WorkerThread public byte[] readData(@IntRange(from=0, to=65535) int); - method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) @WorkerThread public int writeData(@IntRange(from=0, to=65535) int, @NonNull byte[]); - field public static final int CLEAR_DATA_FAILED_DEVICE_BUSY = -1; // 0xffffffff - field public static final int CLEAR_DATA_FAILED_INTERNAL = 0; // 0x0 - field public static final int CLEAR_DATA_SUCCESS = 1; // 0x1 - field public static final int WRITE_DATA_ERROR_CONNECTION_FAILED = -6; // 0xfffffffa - field public static final int WRITE_DATA_ERROR_DEVICE_BUSY = -9; // 0xfffffff7 - field public static final int WRITE_DATA_ERROR_EMPTY_PAYLOAD = -7; // 0xfffffff9 - field public static final int WRITE_DATA_ERROR_INTERNAL = -1; // 0xffffffff - field public static final int WRITE_DATA_ERROR_INVALID_FILE_ID = -4; // 0xfffffffc - field public static final int WRITE_DATA_ERROR_INVALID_LENGTH = -5; // 0xfffffffb - field public static final int WRITE_DATA_ERROR_NDEF_VALIDATION_FAILED = -8; // 0xfffffff8 - field public static final int WRITE_DATA_ERROR_NFC_NOT_ON = -3; // 0xfffffffd - field public static final int WRITE_DATA_ERROR_RF_ACTIVATED = -2; // 0xfffffffe - field public static final int WRITE_DATA_SUCCESS = 0; // 0x0 - } - - @FlaggedApi("android.nfc.nfc_oem_extension") public final class T4tNdefNfceeCcFileInfo implements android.os.Parcelable { - method public int describeContents(); - method @IntRange(from=15, to=32767) public int getCcFileLength(); - method @IntRange(from=0xffffffff, to=65535) public int getFileId(); - method @IntRange(from=5, to=32767) public int getMaxSize(); - method public int getVersion(); - method public boolean isReadAllowed(); - method public boolean isWriteAllowed(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.nfc.T4tNdefNfceeCcFileInfo> CREATOR; - field public static final int VERSION_2_0 = 32; // 0x20 - field public static final int VERSION_3_0 = 48; // 0x30 - } - -} - -package android.nfc.cardemulation { - - public final class CardEmulation { - method @FlaggedApi("android.permission.flags.wallet_role_enabled") @Nullable @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public static android.content.ComponentName getPreferredPaymentService(@NonNull android.content.Context); - method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public java.util.List<android.nfc.cardemulation.ApduServiceInfo> getServices(@NonNull String, int); - method @FlaggedApi("android.nfc.nfc_override_recover_routing_table") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void overrideRoutingTable(@NonNull android.app.Activity, int, int); - method @FlaggedApi("android.nfc.nfc_override_recover_routing_table") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void recoverRoutingTable(@NonNull android.app.Activity); - method @FlaggedApi("android.nfc.enable_card_emulation_euicc") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public int setDefaultNfcSubscriptionId(int); - method @FlaggedApi("android.nfc.nfc_set_service_enabled_for_category_other") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public int setServiceEnabledForCategoryOther(@NonNull android.content.ComponentName, boolean); - field @FlaggedApi("android.nfc.nfc_set_service_enabled_for_category_other") public static final int SET_SERVICE_ENABLED_STATUS_FAILURE_ALREADY_SET = 3; // 0x3 - field @FlaggedApi("android.nfc.nfc_set_service_enabled_for_category_other") public static final int SET_SERVICE_ENABLED_STATUS_FAILURE_FEATURE_UNSUPPORTED = 1; // 0x1 - field @FlaggedApi("android.nfc.nfc_set_service_enabled_for_category_other") public static final int SET_SERVICE_ENABLED_STATUS_FAILURE_INVALID_SERVICE = 2; // 0x2 - field @FlaggedApi("android.nfc.nfc_set_service_enabled_for_category_other") public static final int SET_SERVICE_ENABLED_STATUS_FAILURE_UNKNOWN_ERROR = 4; // 0x4 - field @FlaggedApi("android.nfc.nfc_set_service_enabled_for_category_other") public static final int SET_SERVICE_ENABLED_STATUS_OK = 0; // 0x0 - field @FlaggedApi("android.nfc.enable_card_emulation_euicc") public static final int SET_SUBSCRIPTION_ID_STATUS_FAILED_INTERNAL_ERROR = 2; // 0x2 - field @FlaggedApi("android.nfc.enable_card_emulation_euicc") public static final int SET_SUBSCRIPTION_ID_STATUS_FAILED_INVALID_SUBSCRIPTION_ID = 1; // 0x1 - field @FlaggedApi("android.nfc.enable_card_emulation_euicc") public static final int SET_SUBSCRIPTION_ID_STATUS_FAILED_NOT_SUPPORTED = 3; // 0x3 - field @FlaggedApi("android.nfc.enable_card_emulation_euicc") public static final int SET_SUBSCRIPTION_ID_STATUS_SUCCESS = 0; // 0x0 - field @FlaggedApi("android.nfc.enable_card_emulation_euicc") public static final int SET_SUBSCRIPTION_ID_STATUS_UNKNOWN = -1; // 0xffffffff - } - -} - diff --git a/nfc/api/system-lint-baseline.txt b/nfc/api/system-lint-baseline.txt deleted file mode 100644 index c7a618125add..000000000000 --- a/nfc/api/system-lint-baseline.txt +++ /dev/null @@ -1,119 +0,0 @@ -// Baseline format: 1.0 -BroadcastBehavior: android.nfc.NfcAdapter#ACTION_ADAPTER_STATE_CHANGED: - Field 'ACTION_ADAPTER_STATE_CHANGED' is missing @BroadcastBehavior -BroadcastBehavior: android.nfc.NfcAdapter#ACTION_PREFERRED_PAYMENT_CHANGED: - Field 'ACTION_PREFERRED_PAYMENT_CHANGED' is missing @BroadcastBehavior -BroadcastBehavior: android.nfc.NfcAdapter#ACTION_REQUIRE_UNLOCK_FOR_NFC: - Field 'ACTION_REQUIRE_UNLOCK_FOR_NFC' is missing @BroadcastBehavior -BroadcastBehavior: android.nfc.NfcAdapter#ACTION_TRANSACTION_DETECTED: - Field 'ACTION_TRANSACTION_DETECTED' is missing @BroadcastBehavior - - -CallbackMethodName: android.nfc.NfcOemExtension.Callback#shouldSkipRoutingChange(): - Callback method names must follow the on<Something> style: shouldSkipRoutingChange - - -MethodNameTense: android.nfc.NfcOemExtension.Callback#onEnable(): - Unexpected tense; probably meant `enabled`, was `onEnable` - - -MissingNullability: android.nfc.cardemulation.CardEmulation#overrideRoutingTable(android.app.Activity, String, String) parameter #1: - Missing nullability on parameter `protocol` in method `overrideRoutingTable` -MissingNullability: android.nfc.cardemulation.CardEmulation#overrideRoutingTable(android.app.Activity, String, String) parameter #2: - Missing nullability on parameter `technology` in method `overrideRoutingTable` -MissingNullability: android.nfc.cardemulation.OffHostApduService#onBind(android.content.Intent): - Missing nullability on method `onBind` return -MissingNullability: android.nfc.cardemulation.OffHostApduService#onBind(android.content.Intent) parameter #0: - Missing nullability on parameter `intent` in method `onBind` - - -RequiresPermission: android.nfc.NfcAdapter#disableForegroundDispatch(android.app.Activity): - Method 'disableForegroundDispatch' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.NfcAdapter#enableForegroundDispatch(android.app.Activity, android.app.PendingIntent, android.content.IntentFilter[], String[][]): - Method 'enableForegroundDispatch' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.cardemulation.CardEmulation#isDefaultServiceForAid(android.content.ComponentName, String): - Method 'isDefaultServiceForAid' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.cardemulation.CardEmulation#isDefaultServiceForCategory(android.content.ComponentName, String): - Method 'isDefaultServiceForCategory' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.cardemulation.CardEmulation#setOffHostForService(android.content.ComponentName, String): - Method 'setOffHostForService' documentation mentions permissions already declared by @RequiresPermission -RequiresPermission: android.nfc.tech.IsoDep#getTimeout(): - Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.IsoDep#setTimeout(int): - Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.IsoDep#transceive(byte[]): - Method 'transceive' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#authenticateSectorWithKeyA(int, byte[]): - Method 'authenticateSectorWithKeyA' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#authenticateSectorWithKeyB(int, byte[]): - Method 'authenticateSectorWithKeyB' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#decrement(int, int): - Method 'decrement' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#getTimeout(): - Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#increment(int, int): - Method 'increment' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#readBlock(int): - Method 'readBlock' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#restore(int): - Method 'restore' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#setTimeout(int): - Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#transceive(byte[]): - Method 'transceive' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#transfer(int): - Method 'transfer' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareClassic#writeBlock(int, byte[]): - Method 'writeBlock' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareUltralight#getTimeout(): - Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareUltralight#readPages(int): - Method 'readPages' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareUltralight#setTimeout(int): - Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareUltralight#transceive(byte[]): - Method 'transceive' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.MifareUltralight#writePage(int, byte[]): - Method 'writePage' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.Ndef#getNdefMessage(): - Method 'getNdefMessage' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.Ndef#isWritable(): - Method 'isWritable' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.Ndef#makeReadOnly(): - Method 'makeReadOnly' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.Ndef#writeNdefMessage(android.nfc.NdefMessage): - Method 'writeNdefMessage' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NdefFormatable#format(android.nfc.NdefMessage): - Method 'format' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NdefFormatable#formatReadOnly(android.nfc.NdefMessage): - Method 'formatReadOnly' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NfcA#getTimeout(): - Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NfcA#setTimeout(int): - Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NfcA#transceive(byte[]): - Method 'transceive' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NfcB#transceive(byte[]): - Method 'transceive' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NfcF#getTimeout(): - Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NfcF#setTimeout(int): - Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NfcF#transceive(byte[]): - Method 'transceive' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.NfcV#transceive(byte[]): - Method 'transceive' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.TagTechnology#close(): - Method 'close' documentation mentions permissions without declaring @RequiresPermission -RequiresPermission: android.nfc.tech.TagTechnology#connect(): - Method 'connect' documentation mentions permissions without declaring @RequiresPermission - - -SamShouldBeLast: android.nfc.NfcAdapter#enableReaderMode(android.app.Activity, android.nfc.NfcAdapter.ReaderCallback, int, android.os.Bundle): - SAM-compatible parameters (such as parameter 2, "callback", in android.nfc.NfcAdapter.enableReaderMode) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions -SamShouldBeLast: android.nfc.NfcAdapter#ignore(android.nfc.Tag, int, android.nfc.NfcAdapter.OnTagRemovedListener, android.os.Handler): - SAM-compatible parameters (such as parameter 3, "tagRemovedListener", in android.nfc.NfcAdapter.ignore) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions - - -SdkConstant: android.nfc.NfcAdapter#ACTION_REQUIRE_UNLOCK_FOR_NFC: - Field 'ACTION_REQUIRE_UNLOCK_FOR_NFC' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) diff --git a/nfc/api/system-removed.txt b/nfc/api/system-removed.txt deleted file mode 100644 index c6eaa57b6b06..000000000000 --- a/nfc/api/system-removed.txt +++ /dev/null @@ -1,12 +0,0 @@ -// Signature format: 2.0 -package android.nfc { - - public final class NfcAdapter { - method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean disableNdefPush(); - method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableNdefPush(); - method public void setNdefPushMessage(android.nfc.NdefMessage, android.app.Activity, int); - field public static final int FLAG_NDEF_PUSH_NO_CONFIRM = 1; // 0x1 - } - -} - diff --git a/nfc/api/test-current.txt b/nfc/api/test-current.txt deleted file mode 100644 index d802177e249b..000000000000 --- a/nfc/api/test-current.txt +++ /dev/null @@ -1 +0,0 @@ -// Signature format: 2.0 diff --git a/nfc/api/test-removed.txt b/nfc/api/test-removed.txt deleted file mode 100644 index d802177e249b..000000000000 --- a/nfc/api/test-removed.txt +++ /dev/null @@ -1 +0,0 @@ -// Signature format: 2.0 diff --git a/nfc/jarjar-rules.txt b/nfc/jarjar-rules.txt deleted file mode 100644 index 63a6a58d8ce2..000000000000 --- a/nfc/jarjar-rules.txt +++ /dev/null @@ -1,38 +0,0 @@ -# Used by framework-nfc for proto debug dumping -rule android.app.PendingIntentProto* com.android.nfc.x.@0 -rule android.content.ComponentNameProto* com.android.nfc.x.@0 -rule android.content.IntentProto* com.android.nfc.x.@0 -rule android.content.IntentFilterProto* com.android.nfc.x.@0 -rule android.content.AuthorityEntryProto* com.android.nfc.x.@0 -rule android.content.UriRelativeFilter* com.android.nfc.x.@0 -rule android.nfc.cardemulation.AidGroupProto* com.android.nfc.x.@0 -rule android.nfc.cardemulation.ApduServiceInfoProto* com.android.nfc.x.@0 -rule android.nfc.cardemulation.NfcFServiceInfoProto* com.android.nfc.x.@0 -rule android.nfc.NdefMessageProto* com.android.nfc.x.@0 -rule android.nfc.NdefRecordProto* com.android.nfc.x.@0 -rule com.android.nfc.cardemulation.CardEmulationManagerProto* com.android.nfc.x.@0 -rule com.android.nfc.cardemulation.RegisteredServicesCacheProto* com.android.nfc.x.@0 -rule com.android.nfc.cardemulation.RegisteredNfcFServicesCacheProto* com.android.nfc.x.@0 -rule com.android.nfc.cardemulation.PreferredServicesProto* com.android.nfc.x.@0 -rule com.android.nfc.cardemulation.EnabledNfcFServicesProto* com.android.nfc.x.@0 -rule com.android.nfc.cardemulation.RegisteredAidCacheProto* com.android.nfc.x.@0 -rule com.android.nfc.cardemulation.AidRoutingManagerProto* com.android.nfc.x.@0 -rule com.android.nfc.cardemulation.RegisteredT3tIdentifiersCacheProto* com.android.nfc.x.@0 -rule com.android.nfc.cardemulation.SystemCodeRoutingManagerProto* com.android.nfc.x.@0 -rule com.android.nfc.cardemulation.HostEmulationManagerProto* com.android.nfc.x.@0 -rule com.android.nfc.cardemulation.HostNfcFEmulationManagerProto* com.android.nfc.x.@0 -rule com.android.nfc.NfcServiceDumpProto* com.android.nfc.x.@0 -rule com.android.nfc.DiscoveryParamsProto* com.android.nfc.x.@0 -rule com.android.nfc.NfcDispatcherProto* com.android.nfc.x.@0 -rule android.os.PersistableBundleProto* com.android.nfc.x.@0 - -# Used by framework-nfc for reading trunk stable flags -rule android.nfc.*Flags* com.android.nfc.x.@0 -rule android.nfc.Flags com.android.nfc.x.@0 -rule android.permission.flags.** com.android.nfc.x.@0 - -# Used by framework-nfc for misc utilities -rule android.os.PatternMatcher* com.android.nfc.x.@0 - -rule com.android.incident.Privacy* com.android.nfc.x.@0 -rule com.android.incident.PrivacyFlags* com.android.nfc.x.@0 diff --git a/nfc/java/android/nfc/ApduList.aidl b/nfc/java/android/nfc/ApduList.aidl deleted file mode 100644 index f6236b2bfb3b..000000000000 --- a/nfc/java/android/nfc/ApduList.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2011 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; - -parcelable ApduList;
\ No newline at end of file diff --git a/nfc/java/android/nfc/ApduList.java b/nfc/java/android/nfc/ApduList.java deleted file mode 100644 index 027141d99c30..000000000000 --- a/nfc/java/android/nfc/ApduList.java +++ /dev/null @@ -1,68 +0,0 @@ -package android.nfc; - -import android.os.Parcel; -import android.os.Parcelable; - -import java.util.ArrayList; -import java.util.List; - -/** - * @hide - */ -public class ApduList implements Parcelable { - - private ArrayList<byte[]> commands = new ArrayList<byte[]>(); - - public ApduList() { - } - - public void add(byte[] command) { - commands.add(command); - } - - public List<byte[]> get() { - return commands; - } - - public static final @android.annotation.NonNull Parcelable.Creator<ApduList> CREATOR = - new Parcelable.Creator<ApduList>() { - @Override - public ApduList createFromParcel(Parcel in) { - return new ApduList(in); - } - - @Override - public ApduList[] newArray(int size) { - return new ApduList[size]; - } - }; - - private ApduList(Parcel in) { - int count = in.readInt(); - - for (int i = 0 ; i < count ; i++) { - - int length = in.readInt(); - byte[] cmd = new byte[length]; - in.readByteArray(cmd); - commands.add(cmd); - } - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(commands.size()); - - for (byte[] cmd : commands) { - dest.writeInt(cmd.length); - dest.writeByteArray(cmd); - } - } -} - - diff --git a/nfc/java/android/nfc/AvailableNfcAntenna.aidl b/nfc/java/android/nfc/AvailableNfcAntenna.aidl deleted file mode 100644 index 9d06e2d7d5eb..000000000000 --- a/nfc/java/android/nfc/AvailableNfcAntenna.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2013 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; - -parcelable AvailableNfcAntenna; diff --git a/nfc/java/android/nfc/AvailableNfcAntenna.java b/nfc/java/android/nfc/AvailableNfcAntenna.java deleted file mode 100644 index e76aeb07f106..000000000000 --- a/nfc/java/android/nfc/AvailableNfcAntenna.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (C) 2015 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.NonNull; -import android.annotation.Nullable; -import android.os.Parcel; -import android.os.Parcelable; - -/** - * Represents a single available Nfc antenna - * on an Android device. - */ -public final class AvailableNfcAntenna implements Parcelable { - /** - * Location of the antenna on the Y axis in millimeters. - * 0 is the top-left when the user is facing the screen - * and the device orientation is Portrait. - */ - private final int mLocationX; - /** - * Location of the antenna on the Y axis in millimeters. - * 0 is the top-left when the user is facing the screen - * and the device orientation is Portrait. - */ - private final int mLocationY; - - public AvailableNfcAntenna(int locationX, int locationY) { - this.mLocationX = locationX; - this.mLocationY = locationY; - } - - /** - * Location of the antenna on the X axis in millimeters. - * 0 is the top-left when the user is facing the screen - * and the device orientation is Portrait. - */ - public int getLocationX() { - return mLocationX; - } - - /** - * Location of the antenna on the Y axis in millimeters. - * 0 is the top-left when the user is facing the screen - * and the device orientation is Portrait. - */ - public int getLocationY() { - return mLocationY; - } - - private AvailableNfcAntenna(Parcel in) { - this.mLocationX = in.readInt(); - this.mLocationY = in.readInt(); - } - - public static final @android.annotation.NonNull Parcelable.Creator<AvailableNfcAntenna> - CREATOR = new Parcelable.Creator<AvailableNfcAntenna>() { - @Override - public AvailableNfcAntenna createFromParcel(Parcel in) { - return new AvailableNfcAntenna(in); - } - - @Override - public AvailableNfcAntenna[] newArray(int size) { - return new AvailableNfcAntenna[size]; - } - }; - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeInt(mLocationX); - dest.writeInt(mLocationY); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + mLocationX; - result = prime * result + mLocationY; - return result; - } - - /** - * Returns true if the specified AvailableNfcAntenna contains - * identical specifications. - */ - @Override - public boolean equals(@Nullable Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - AvailableNfcAntenna other = (AvailableNfcAntenna) obj; - if (this.mLocationX != other.mLocationX) return false; - return this.mLocationY == other.mLocationY; - } - - @Override - public String toString() { - return "AvailableNfcAntenna " + "x: " + mLocationX + " y: " + mLocationY; - } -} diff --git a/nfc/java/android/nfc/ComponentNameAndUser.aidl b/nfc/java/android/nfc/ComponentNameAndUser.aidl deleted file mode 100644 index e677998a7970..000000000000 --- a/nfc/java/android/nfc/ComponentNameAndUser.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 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; - -parcelable ComponentNameAndUser;
\ No newline at end of file diff --git a/nfc/java/android/nfc/ComponentNameAndUser.java b/nfc/java/android/nfc/ComponentNameAndUser.java deleted file mode 100644 index 59e6c62926c9..000000000000 --- a/nfc/java/android/nfc/ComponentNameAndUser.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (C) 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.UserIdInt; -import android.content.ComponentName; -import android.os.Parcel; -import android.os.Parcelable; - -import java.util.Objects; - -/** - * @hide - */ -public class ComponentNameAndUser implements Parcelable { - @UserIdInt private final int mUserId; - private ComponentName mComponentName; - - public ComponentNameAndUser(@UserIdInt int userId, ComponentName componentName) { - mUserId = userId; - mComponentName = componentName; - } - - /** - * @hide - */ - public int describeContents() { - return 0; - } - - /** - * @hide - */ - public void writeToParcel(Parcel out, int flags) { - out.writeInt(mUserId); - out.writeParcelable(mComponentName, flags); - } - - public static final Parcelable.Creator<ComponentNameAndUser> CREATOR = - new Parcelable.Creator<ComponentNameAndUser>() { - public ComponentNameAndUser createFromParcel(Parcel in) { - return new ComponentNameAndUser(in); - } - - public ComponentNameAndUser[] newArray(int size) { - return new ComponentNameAndUser[size]; - } - }; - - private ComponentNameAndUser(Parcel in) { - mUserId = in.readInt(); - mComponentName = in.readParcelable(null, ComponentName.class); - } - - @UserIdInt - public int getUserId() { - return mUserId; - } - - public ComponentName getComponentName() { - return mComponentName; - } - - @Override - public String toString() { - return mComponentName + " for user id: " + mUserId; - } - - @Override - public boolean equals(Object obj) { - if (obj != null && obj instanceof ComponentNameAndUser) { - ComponentNameAndUser other = (ComponentNameAndUser) obj; - return other.getUserId() == mUserId - && Objects.equals(other.getComponentName(), mComponentName); - } - return false; - } - - @Override - public int hashCode() { - if (mComponentName == null) { - return mUserId; - } - return mComponentName.hashCode() + mUserId; - } -} diff --git a/nfc/java/android/nfc/Constants.java b/nfc/java/android/nfc/Constants.java deleted file mode 100644 index 9b11e2d30ed8..000000000000 --- a/nfc/java/android/nfc/Constants.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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; - -import android.provider.Settings; - -/** - * @hide - * TODO(b/303286040): Holds @hide API constants. Formalize these APIs. - */ -public final class Constants { - private Constants() { } - - public static final String SETTINGS_SECURE_NFC_PAYMENT_FOREGROUND = "nfc_payment_foreground"; - public static final String SETTINGS_SECURE_NFC_PAYMENT_DEFAULT_COMPONENT = "nfc_payment_default_component"; - public static final String FEATURE_NFC_ANY = "android.hardware.nfc.any"; - - /** - * @hide constant copied from {@link Settings.Global} - * TODO(b/274636414): Migrate to official API in Android V. - */ - public static final String SETTINGS_SATELLITE_MODE_RADIOS = "satellite_mode_radios"; - /** - * @hide constant copied from {@link Settings.Global} - * TODO(b/274636414): Migrate to official API in Android V. - */ - public static final String SETTINGS_SATELLITE_MODE_ENABLED = "satellite_mode_enabled"; -} diff --git a/nfc/java/android/nfc/Entry.aidl b/nfc/java/android/nfc/Entry.aidl deleted file mode 100644 index 148c4ec86845..000000000000 --- a/nfc/java/android/nfc/Entry.aidl +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (C) 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; - -parcelable Entry;
\ No newline at end of file diff --git a/nfc/java/android/nfc/Entry.java b/nfc/java/android/nfc/Entry.java deleted file mode 100644 index aa5ba58e7179..000000000000 --- a/nfc/java/android/nfc/Entry.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 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.NonNull; -import android.os.Parcel; -import android.os.Parcelable; - - -/** @hide */ -public final class Entry implements Parcelable { - private final byte mType; - private final byte mNfceeId; - private final String mEntry; - private final String mRoutingType; - - public Entry(String entry, byte type, byte nfceeId, String routingType) { - mEntry = entry; - mType = type; - mNfceeId = nfceeId; - mRoutingType = routingType; - } - - public byte getType() { - return mType; - } - - public byte getNfceeId() { - return mNfceeId; - } - - public String getEntry() { - return mEntry; - } - - public String getRoutingType() { - return mRoutingType; - } - - @Override - public int describeContents() { - return 0; - } - - private Entry(Parcel in) { - this.mEntry = in.readString(); - this.mNfceeId = in.readByte(); - this.mType = in.readByte(); - this.mRoutingType = in.readString(); - } - - public static final @NonNull Parcelable.Creator<Entry> CREATOR = - new Parcelable.Creator<Entry>() { - @Override - public Entry createFromParcel(Parcel in) { - return new Entry(in); - } - - @Override - public Entry[] newArray(int size) { - return new Entry[size]; - } - }; - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeString(mEntry); - dest.writeByte(mNfceeId); - dest.writeByte(mType); - dest.writeString(mRoutingType); - } -} diff --git a/nfc/java/android/nfc/ErrorCodes.java b/nfc/java/android/nfc/ErrorCodes.java deleted file mode 100644 index d2c81cd27d90..000000000000 --- a/nfc/java/android/nfc/ErrorCodes.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (C) 2010, 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.compat.annotation.UnsupportedAppUsage; - -/** - * This class defines all the error codes that can be returned by the service - * and producing an exception on the application level. These are needed since - * binders does not support exceptions. - * - * @hide - */ -public class ErrorCodes { - - @UnsupportedAppUsage - public static boolean isError(int code) { - if (code < 0) { - return true; - } else { - return false; - } - } - - public static String asString(int code) { - switch (code) { - case SUCCESS: return "SUCCESS"; - case ERROR_IO: return "IO"; - case ERROR_CANCELLED: return "CANCELLED"; - case ERROR_TIMEOUT: return "TIMEOUT"; - case ERROR_BUSY: return "BUSY"; - case ERROR_CONNECT: return "CONNECT/DISCONNECT"; -// case ERROR_DISCONNECT: return "DISCONNECT"; - case ERROR_READ: return "READ"; - case ERROR_WRITE: return "WRITE"; - case ERROR_INVALID_PARAM: return "INVALID_PARAM"; - case ERROR_INSUFFICIENT_RESOURCES: return "INSUFFICIENT_RESOURCES"; - case ERROR_SOCKET_CREATION: return "SOCKET_CREATION"; - case ERROR_SOCKET_NOT_CONNECTED: return "SOCKET_NOT_CONNECTED"; - case ERROR_BUFFER_TO_SMALL: return "BUFFER_TO_SMALL"; - case ERROR_SAP_USED: return "SAP_USED"; - case ERROR_SERVICE_NAME_USED: return "SERVICE_NAME_USED"; - case ERROR_SOCKET_OPTIONS: return "SOCKET_OPTIONS"; - case ERROR_NFC_ON: return "NFC_ON"; - case ERROR_NOT_INITIALIZED: return "NOT_INITIALIZED"; - case ERROR_SE_ALREADY_SELECTED: return "SE_ALREADY_SELECTED"; - case ERROR_SE_CONNECTED: return "SE_CONNECTED"; - case ERROR_NO_SE_CONNECTED: return "NO_SE_CONNECTED"; - case ERROR_NOT_SUPPORTED: return "NOT_SUPPORTED"; - default: return "UNKNOWN ERROR"; - } - } - - public static final int SUCCESS = 0; - - public static final int ERROR_IO = -1; - - public static final int ERROR_CANCELLED = -2; - - public static final int ERROR_TIMEOUT = -3; - - public static final int ERROR_BUSY = -4; - - public static final int ERROR_CONNECT = -5; - - public static final int ERROR_DISCONNECT = -5; - - public static final int ERROR_READ = -6; - - public static final int ERROR_WRITE = -7; - - public static final int ERROR_INVALID_PARAM = -8; - - public static final int ERROR_INSUFFICIENT_RESOURCES = -9; - - public static final int ERROR_SOCKET_CREATION = -10; - - public static final int ERROR_SOCKET_NOT_CONNECTED = -11; - - public static final int ERROR_BUFFER_TO_SMALL = -12; - - public static final int ERROR_SAP_USED = -13; - - public static final int ERROR_SERVICE_NAME_USED = -14; - - public static final int ERROR_SOCKET_OPTIONS = -15; - - public static final int ERROR_NFC_ON = -16; - - public static final int ERROR_NOT_INITIALIZED = -17; - - public static final int ERROR_SE_ALREADY_SELECTED = -18; - - public static final int ERROR_SE_CONNECTED = -19; - - public static final int ERROR_NO_SE_CONNECTED = -20; - - public static final int ERROR_NOT_SUPPORTED = -21; - -} diff --git a/nfc/java/android/nfc/FormatException.java b/nfc/java/android/nfc/FormatException.java deleted file mode 100644 index a57de1e0e21a..000000000000 --- a/nfc/java/android/nfc/FormatException.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2010, 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; - -public class FormatException extends Exception { - public FormatException() { - super(); - } - - public FormatException(String message) { - super(message); - } - - public FormatException(String message, Throwable e) { - super(message, e); - } -} diff --git a/nfc/java/android/nfc/IAppCallback.aidl b/nfc/java/android/nfc/IAppCallback.aidl deleted file mode 100644 index b06bf06d5197..000000000000 --- a/nfc/java/android/nfc/IAppCallback.aidl +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2011 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.nfc.Tag; - -/** - * @hide - */ -interface IAppCallback -{ - oneway void onTagDiscovered(in Tag tag); -} diff --git a/nfc/java/android/nfc/INfcAdapter.aidl b/nfc/java/android/nfc/INfcAdapter.aidl deleted file mode 100644 index ac0a5aaaa195..000000000000 --- a/nfc/java/android/nfc/INfcAdapter.aidl +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (C) 2010 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.app.PendingIntent; -import android.content.IntentFilter; -import android.nfc.Entry; -import android.nfc.NdefMessage; -import android.nfc.Tag; -import android.nfc.TechListParcel; -import android.nfc.IAppCallback; -import android.nfc.INfcAdapterExtras; -import android.nfc.INfcControllerAlwaysOnListener; -import android.nfc.INfcVendorNciCallback; -import android.nfc.INfcTag; -import android.nfc.INfcCardEmulation; -import android.nfc.INfcFCardEmulation; -import android.nfc.INfcOemExtensionCallback; -import android.nfc.INfcUnlockHandler; -import android.nfc.IT4tNdefNfcee; -import android.nfc.ITagRemovedCallback; -import android.nfc.INfcDta; -import android.nfc.INfcWlcStateListener; -import android.nfc.NfcAntennaInfo; -import android.nfc.WlcListenerDeviceInfo; -import android.nfc.cardemulation.PollingFrame; -import android.os.Bundle; - -/** - * @hide - */ -interface INfcAdapter -{ - INfcTag getNfcTagInterface(); - INfcCardEmulation getNfcCardEmulationInterface(); - INfcFCardEmulation getNfcFCardEmulationInterface(); - INfcAdapterExtras getNfcAdapterExtrasInterface(in String pkg); - INfcDta getNfcDtaInterface(in String pkg); - int getState(); - boolean disable(boolean saveState, in String pkg); - boolean enable(in String pkg); - int pausePolling(long timeoutInMs); - int resumePolling(); - - void setForegroundDispatch(in PendingIntent intent, - in IntentFilter[] filters, in TechListParcel techLists); - void setAppCallback(in IAppCallback callback); - - boolean ignore(int nativeHandle, int debounceMs, ITagRemovedCallback callback); - - void dispatch(in Tag tag); - - void setReaderMode (IBinder b, IAppCallback callback, int flags, in Bundle extras, String pkg); - - void addNfcUnlockHandler(INfcUnlockHandler unlockHandler, in int[] techList); - void removeNfcUnlockHandler(INfcUnlockHandler unlockHandler); - - void verifyNfcPermission(); - boolean isNfcSecureEnabled(); - boolean deviceSupportsNfcSecure(); - boolean setNfcSecure(boolean enable); - NfcAntennaInfo getNfcAntennaInfo(); - - void setControllerAlwaysOn(int mode); - boolean isControllerAlwaysOn(); - boolean isControllerAlwaysOnSupported(); - void registerControllerAlwaysOnListener(in INfcControllerAlwaysOnListener listener); - void unregisterControllerAlwaysOnListener(in INfcControllerAlwaysOnListener listener); - @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)") - boolean isTagIntentAppPreferenceSupported(); - @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)") - Map getTagIntentAppPreferenceForUser(int userId); - @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)") - int setTagIntentAppPreferenceForUser(int userId, String pkg, boolean allow); - - boolean isReaderOptionEnabled(); - boolean isReaderOptionSupported(); - @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)") - boolean enableReaderOption(boolean enable, in String pkg); - boolean isObserveModeSupported(); - boolean isObserveModeEnabled(); - boolean setObserveMode(boolean enabled, String pkg); - - @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)") - boolean setWlcEnabled(boolean enable); - boolean isWlcEnabled(); - void registerWlcStateListener(in INfcWlcStateListener listener); - void unregisterWlcStateListener(in INfcWlcStateListener listener); - WlcListenerDeviceInfo getWlcListenerDeviceInfo(); - - void updateDiscoveryTechnology(IBinder b, int pollFlags, int listenFlags, String pkg); - - void notifyPollingLoop(in PollingFrame frame); - void notifyHceDeactivated(); - void notifyTestHceData(in int technology, in byte[] data); - int sendVendorNciMessage(int mt, int gid, int oid, in byte[] payload); - void registerVendorExtensionCallback(in INfcVendorNciCallback callbacks); - void unregisterVendorExtensionCallback(in INfcVendorNciCallback callbacks); - void registerOemExtensionCallback(INfcOemExtensionCallback callbacks); - void unregisterOemExtensionCallback(INfcOemExtensionCallback callbacks); - void clearPreference(); - void setScreenState(); - void checkFirmware(); - Map fetchActiveNfceeList(); - void triggerInitialization(); - boolean getSettingStatus(); - boolean isTagPresent(); - List<Entry> getRoutingTableEntryList(); - void indicateDataMigration(boolean inProgress, String pkg); - int commitRouting(); - boolean isTagIntentAllowed(in String pkg, in int Userid); - IT4tNdefNfcee getT4tNdefNfceeInterface(); - long getMaxPausePollingTimeoutMs(); -} diff --git a/nfc/java/android/nfc/INfcAdapterExtras.aidl b/nfc/java/android/nfc/INfcAdapterExtras.aidl deleted file mode 100644 index cde57c58ca1f..000000000000 --- a/nfc/java/android/nfc/INfcAdapterExtras.aidl +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2011 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.os.Bundle; - - -/** - * {@hide} - */ -interface INfcAdapterExtras { - @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) - Bundle open(in String pkg, IBinder b); - @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) - Bundle close(in String pkg, IBinder b); - @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) - Bundle transceive(in String pkg, in byte[] data_in); - @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) - int getCardEmulationRoute(in String pkg); - @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) - void setCardEmulationRoute(in String pkg, int route); - @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) - void authenticate(in String pkg, in byte[] token); - @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) - String getDriverName(in String pkg); -} diff --git a/nfc/java/android/nfc/INfcCardEmulation.aidl b/nfc/java/android/nfc/INfcCardEmulation.aidl deleted file mode 100644 index 00ceaa9801d8..000000000000 --- a/nfc/java/android/nfc/INfcCardEmulation.aidl +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2013 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.content.ComponentName; -import android.nfc.INfcEventCallback; - -import android.nfc.cardemulation.AidGroup; -import android.nfc.cardemulation.ApduServiceInfo; -import android.os.RemoteCallback; - -/** - * @hide - */ -interface INfcCardEmulation -{ - boolean isDefaultServiceForCategory(int userHandle, in ComponentName service, String category); - boolean isDefaultServiceForAid(int userHandle, in ComponentName service, String aid); - boolean setDefaultServiceForCategory(int userHandle, in ComponentName service, String category); - boolean setDefaultForNextTap(int userHandle, in ComponentName service); - boolean setShouldDefaultToObserveModeForService(int userId, in android.content.ComponentName service, boolean enable); - boolean registerAidGroupForService(int userHandle, in ComponentName service, in AidGroup aidGroup); - boolean registerPollingLoopFilterForService(int userHandle, in ComponentName service, in String pollingLoopFilter, boolean autoTransact); - boolean registerPollingLoopPatternFilterForService(int userHandle, in ComponentName service, in String pollingLoopPatternFilter, boolean autoTransact); - boolean setOffHostForService(int userHandle, in ComponentName service, in String offHostSecureElement); - boolean unsetOffHostForService(int userHandle, in ComponentName service); - AidGroup getAidGroupForService(int userHandle, in ComponentName service, String category); - boolean removeAidGroupForService(int userHandle, in ComponentName service, String category); - boolean removePollingLoopFilterForService(int userHandle, in ComponentName service, in String pollingLoopFilter); - boolean removePollingLoopPatternFilterForService(int userHandle, in ComponentName service, in String pollingLoopPatternFilter); - List<ApduServiceInfo> getServices(int userHandle, in String category); - boolean setPreferredService(in ComponentName service); - boolean unsetPreferredService(); - boolean supportsAidPrefixRegistration(); - ApduServiceInfo getPreferredPaymentService(int userHandle); - int setServiceEnabledForCategoryOther(int userHandle, in ComponentName app, boolean status); - boolean isDefaultPaymentRegistered(); - - void overrideRoutingTable(int userHandle, String protocol, String technology, in String pkg); - void recoverRoutingTable(int userHandle); - boolean isEuiccSupported(); - int getDefaultNfcSubscriptionId(in String pkg); - int setDefaultNfcSubscriptionId(int subscriptionId, in String pkg); - void setAutoChangeStatus(boolean state); - boolean isAutoChangeEnabled(); - List<String> getRoutingStatus(); - void overwriteRoutingTable(int userHandle, String emptyAid, String protocol, String tech, String sc); - - void registerNfcEventCallback(in INfcEventCallback listener); - void unregisterNfcEventCallback(in INfcEventCallback listener); -} diff --git a/nfc/java/android/nfc/INfcControllerAlwaysOnListener.aidl b/nfc/java/android/nfc/INfcControllerAlwaysOnListener.aidl deleted file mode 100644 index 1bb7680d2fed..000000000000 --- a/nfc/java/android/nfc/INfcControllerAlwaysOnListener.aidl +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2021 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; - -/** - * @hide - */ -oneway interface INfcControllerAlwaysOnListener { - /** - * Called whenever the controller always on state changes - * - * @param isEnabled true if the state is enabled, false otherwise - */ - void onControllerAlwaysOnChanged(boolean isEnabled); -} diff --git a/nfc/java/android/nfc/INfcDta.aidl b/nfc/java/android/nfc/INfcDta.aidl deleted file mode 100644 index 4cc59271362b..000000000000 --- a/nfc/java/android/nfc/INfcDta.aidl +++ /dev/null @@ -1,34 +0,0 @@ - /* - * Copyright (C) 2017 NXP Semiconductors - * - * 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.os.Bundle; - -/** - * {@hide} - */ -interface INfcDta { - - void enableDta(); - void disableDta(); - boolean enableServer(String serviceName, int serviceSap, int miu, - int rwSize,int testCaseId); - void disableServer(); - boolean enableClient(String serviceName, int miu, int rwSize, - int testCaseId); - void disableClient(); - boolean registerMessageService(String msgServiceName); -} diff --git a/nfc/java/android/nfc/INfcEventCallback.aidl b/nfc/java/android/nfc/INfcEventCallback.aidl deleted file mode 100644 index af1fa2fb2456..000000000000 --- a/nfc/java/android/nfc/INfcEventCallback.aidl +++ /dev/null @@ -1,16 +0,0 @@ -package android.nfc; - -import android.nfc.ComponentNameAndUser; - -/** - * @hide - */ -oneway interface INfcEventCallback { - void onPreferredServiceChanged(in ComponentNameAndUser ComponentNameAndUser); - void onObserveModeStateChanged(boolean isEnabled); - void onAidConflictOccurred(in String aid); - void onAidNotRouted(in String aid); - void onNfcStateChanged(in int nfcState); - void onRemoteFieldChanged(boolean isDetected); - void onInternalErrorReported(in int errorType); -}
\ No newline at end of file diff --git a/nfc/java/android/nfc/INfcFCardEmulation.aidl b/nfc/java/android/nfc/INfcFCardEmulation.aidl deleted file mode 100644 index 124bfac4f0d0..000000000000 --- a/nfc/java/android/nfc/INfcFCardEmulation.aidl +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2015 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.content.ComponentName; -import android.nfc.cardemulation.NfcFServiceInfo; - -/** - * @hide - */ -interface INfcFCardEmulation -{ - String getSystemCodeForService(int userHandle, in ComponentName service); - boolean registerSystemCodeForService(int userHandle, in ComponentName service, String systemCode); - boolean removeSystemCodeForService(int userHandle, in ComponentName service); - String getNfcid2ForService(int userHandle, in ComponentName service); - boolean setNfcid2ForService(int userHandle, in ComponentName service, String nfcid2); - boolean enableNfcFForegroundService(in ComponentName service); - boolean disableNfcFForegroundService(); - List<NfcFServiceInfo> getNfcFServices(int userHandle); - int getMaxNumOfRegisterableSystemCodes(); -} diff --git a/nfc/java/android/nfc/INfcOemExtensionCallback.aidl b/nfc/java/android/nfc/INfcOemExtensionCallback.aidl deleted file mode 100644 index 357d3229306c..000000000000 --- a/nfc/java/android/nfc/INfcOemExtensionCallback.aidl +++ /dev/null @@ -1,59 +0,0 @@ -/* - * 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.content.ComponentName; -import android.nfc.cardemulation.ApduServiceInfo; -import android.nfc.NdefMessage; -import android.nfc.OemLogItems; -import android.nfc.Tag; -import android.os.ResultReceiver; - -import java.util.List; - -/** - * @hide - */ -interface INfcOemExtensionCallback { - void onTagConnected(boolean connected); - void onStateUpdated(int state); - void onApplyRouting(in ResultReceiver isSkipped); - void onNdefRead(in ResultReceiver isSkipped); - void onEnable(in ResultReceiver isAllowed); - void onDisable(in ResultReceiver isAllowed); - void onBootStarted(); - void onEnableStarted(); - void onDisableStarted(); - void onBootFinished(int status); - void onEnableFinished(int status); - void onDisableFinished(int status); - void onTagDispatch(in ResultReceiver isSkipped); - void onRoutingChanged(in ResultReceiver isSkipped); - void onHceEventReceived(int action); - void onReaderOptionChanged(boolean enabled); - void onCardEmulationActivated(boolean isActivated); - void onRfFieldDetected(boolean isActive); - void onRfDiscoveryStarted(boolean isDiscoveryStarted); - void onEeListenActivated(boolean isActivated); - void onEeUpdated(); - void onGetOemAppSearchIntent(in List<String> firstPackage, in ResultReceiver intentConsumer); - void onNdefMessage(in Tag tag, in NdefMessage message, in ResultReceiver hasOemExecutableContent); - void onLaunchHceAppChooserActivity(in String selectedAid, in List<ApduServiceInfo> services, in ComponentName failedComponent, in String category); - void onLaunchHceTapAgainActivity(in ApduServiceInfo service, in String category); - void onRoutingTableFull(); - void onLogEventNotified(in OemLogItems item); - void onExtractOemPackages(in NdefMessage message, in ResultReceiver packageReceiver); -} diff --git a/nfc/java/android/nfc/INfcTag.aidl b/nfc/java/android/nfc/INfcTag.aidl deleted file mode 100644 index 170df71385bb..000000000000 --- a/nfc/java/android/nfc/INfcTag.aidl +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2010 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.nfc.NdefMessage; -import android.nfc.Tag; -import android.nfc.TransceiveResult; - -/** - * @hide - */ -interface INfcTag -{ - int connect(int nativeHandle, int technology); - int reconnect(int nativeHandle); - int[] getTechList(int nativeHandle); - boolean isNdef(int nativeHandle); - boolean isPresent(int nativeHandle); - TransceiveResult transceive(int nativeHandle, in byte[] data, boolean raw); - - NdefMessage ndefRead(int nativeHandle); - int ndefWrite(int nativeHandle, in NdefMessage msg); - int ndefMakeReadOnly(int nativeHandle); - boolean ndefIsWritable(int nativeHandle); - int formatNdef(int nativeHandle, in byte[] key); - Tag rediscover(int nativehandle); - - int setTimeout(int technology, int timeout); - int getTimeout(int technology); - void resetTimeouts(); - boolean canMakeReadOnly(int ndefType); - int getMaxTransceiveLength(int technology); - boolean getExtendedLengthApdusSupported(); - - boolean isTagUpToDate(long cookie); -} diff --git a/nfc/java/android/nfc/INfcUnlockHandler.aidl b/nfc/java/android/nfc/INfcUnlockHandler.aidl deleted file mode 100644 index e1cace987dc3..000000000000 --- a/nfc/java/android/nfc/INfcUnlockHandler.aidl +++ /dev/null @@ -1,12 +0,0 @@ -package android.nfc; - -import android.nfc.Tag; - -/** - * @hide - */ -interface INfcUnlockHandler { - - boolean onUnlockAttempted(in Tag tag); - -} diff --git a/nfc/java/android/nfc/INfcVendorNciCallback.aidl b/nfc/java/android/nfc/INfcVendorNciCallback.aidl deleted file mode 100644 index 821dc6f6c868..000000000000 --- a/nfc/java/android/nfc/INfcVendorNciCallback.aidl +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 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; - -/** - * @hide - */ -oneway interface INfcVendorNciCallback { - void onVendorResponseReceived(int gid, int oid, in byte[] payload); - void onVendorNotificationReceived(int gid, int oid, in byte[] payload); -} diff --git a/nfc/java/android/nfc/INfcWlcStateListener.aidl b/nfc/java/android/nfc/INfcWlcStateListener.aidl deleted file mode 100644 index 584eb9a128b4..000000000000 --- a/nfc/java/android/nfc/INfcWlcStateListener.aidl +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 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; - -import android.nfc.WlcListenerDeviceInfo; -/** - * @hide - */ -oneway interface INfcWlcStateListener { - /** - * Called whenever NFC WLC state changes - * - * @param wlcListenerDeviceInfo NFC wlc listener information - */ - void onWlcStateChanged(in WlcListenerDeviceInfo wlcListenerDeviceInfo); -} diff --git a/nfc/java/android/nfc/IT4tNdefNfcee.aidl b/nfc/java/android/nfc/IT4tNdefNfcee.aidl deleted file mode 100644 index b4cda5b022fb..000000000000 --- a/nfc/java/android/nfc/IT4tNdefNfcee.aidl +++ /dev/null @@ -1,33 +0,0 @@ -/****************************************************************************** - * - * Copyright (C) 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.nfc.T4tNdefNfceeCcFileInfo; - -/** - * @hide - */ -interface IT4tNdefNfcee { - int writeData(in int fileId, in byte[] data); - byte[] readData(in int fileId); - int clearNdefData(); - boolean isNdefOperationOngoing(); - boolean isNdefNfceeEmulationSupported(); - T4tNdefNfceeCcFileInfo readCcfile(); -} diff --git a/nfc/java/android/nfc/ITagRemovedCallback.aidl b/nfc/java/android/nfc/ITagRemovedCallback.aidl deleted file mode 100644 index 2a06ff314b22..000000000000 --- a/nfc/java/android/nfc/ITagRemovedCallback.aidl +++ /dev/null @@ -1,8 +0,0 @@ -package android.nfc; - -/** - * @hide - */ -oneway interface ITagRemovedCallback { - void onTagRemoved(); -} diff --git a/nfc/java/android/nfc/NdefMessage.aidl b/nfc/java/android/nfc/NdefMessage.aidl deleted file mode 100644 index 378b9d05b385..000000000000 --- a/nfc/java/android/nfc/NdefMessage.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2010 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; - -parcelable NdefMessage; diff --git a/nfc/java/android/nfc/NdefMessage.java b/nfc/java/android/nfc/NdefMessage.java deleted file mode 100644 index 553f6c01b016..000000000000 --- a/nfc/java/android/nfc/NdefMessage.java +++ /dev/null @@ -1,272 +0,0 @@ -/* - * Copyright (C) 2010 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.Nullable; -import android.os.Parcel; -import android.os.Parcelable; -import android.util.proto.ProtoOutputStream; - -import java.nio.ByteBuffer; -import java.util.Arrays; - -/** - * Represents an immutable NDEF Message. - * <p> - * NDEF (NFC Data Exchange Format) is a light-weight binary format, - * used to encapsulate typed data. It is specified by the NFC Forum, - * for transmission and storage with NFC, however it is transport agnostic. - * <p> - * NDEF defines messages and records. An NDEF Record contains - * typed data, such as MIME-type media, a URI, or a custom - * application payload. An NDEF Message is a container for - * one or more NDEF Records. - * <p> - * When an Android device receives an NDEF Message - * (for example by reading an NFC tag) it processes it through - * a dispatch mechanism to determine an activity to launch. - * The type of the <em>first</em> record in the message has - * special importance for message dispatch, so design this record - * carefully. - * <p> - * Use {@link #NdefMessage(byte[])} to construct an NDEF Message from - * binary data, or {@link #NdefMessage(NdefRecord[])} to - * construct from one or more {@link NdefRecord}s. - * <p class="note"> - * {@link NdefMessage} and {@link NdefRecord} implementations are - * always available, even on Android devices that do not have NFC hardware. - * <p class="note"> - * {@link NdefRecord}s are intended to be immutable (and thread-safe), - * however they may contain mutable fields. So take care not to modify - * mutable fields passed into constructors, or modify mutable fields - * obtained by getter methods, unless such modification is explicitly - * marked as safe. - * - * @see NfcAdapter#ACTION_NDEF_DISCOVERED - * @see NdefRecord - */ -public final class NdefMessage implements Parcelable { - private final NdefRecord[] mRecords; - - /** - * Construct an NDEF Message by parsing raw bytes.<p> - * Strict validation of the NDEF binary structure is performed: - * there must be at least one record, every record flag must - * be correct, and the total length of the message must match - * the length of the input data.<p> - * This parser can handle chunked records, and converts them - * into logical {@link NdefRecord}s within the message.<p> - * Once the input data has been parsed to one or more logical - * records, basic validation of the tnf, type, id, and payload fields - * of each record is performed, as per the documentation on - * on {@link NdefRecord#NdefRecord(short, byte[], byte[], byte[])}<p> - * If either strict validation of the binary format fails, or - * basic validation during record construction fails, a - * {@link FormatException} is thrown<p> - * Deep inspection of the type, id and payload fields of - * each record is not performed, so it is possible to parse input - * that has a valid binary format and confirms to the basic - * validation requirements of - * {@link NdefRecord#NdefRecord(short, byte[], byte[], byte[])}, - * but fails more strict requirements as specified by the - * NFC Forum. - * - * <p class="note"> - * It is safe to re-use the data byte array after construction: - * this constructor will make an internal copy of all necessary fields. - * - * @param data raw bytes to parse - * @throws FormatException if the data cannot be parsed - */ - public NdefMessage(byte[] data) throws FormatException { - if (data == null) throw new NullPointerException("data is null"); - ByteBuffer buffer = ByteBuffer.wrap(data); - - mRecords = NdefRecord.parse(buffer, false); - - if (buffer.remaining() > 0) { - throw new FormatException("trailing data"); - } - } - - /** - * Construct an NDEF Message from one or more NDEF Records. - * - * @param record first record (mandatory) - * @param records additional records (optional) - */ - public NdefMessage(NdefRecord record, NdefRecord ... records) { - // validate - if (record == null) throw new NullPointerException("record cannot be null"); - - for (NdefRecord r : records) { - if (r == null) { - throw new NullPointerException("record cannot be null"); - } - } - - mRecords = new NdefRecord[1 + records.length]; - mRecords[0] = record; - System.arraycopy(records, 0, mRecords, 1, records.length); - } - - /** - * Construct an NDEF Message from one or more NDEF Records. - * - * @param records one or more records - */ - public NdefMessage(NdefRecord[] records) { - // validate - if (records.length < 1) { - throw new IllegalArgumentException("must have at least one record"); - } - for (NdefRecord r : records) { - if (r == null) { - throw new NullPointerException("records cannot contain null"); - } - } - - mRecords = records; - } - - /** - * Get the NDEF Records inside this NDEF Message.<p> - * An {@link NdefMessage} always has one or more NDEF Records: so the - * following code to retrieve the first record is always safe - * (no need to check for null or array length >= 1): - * <pre> - * NdefRecord firstRecord = ndefMessage.getRecords()[0]; - * </pre> - * - * @return array of one or more NDEF records. - */ - public NdefRecord[] getRecords() { - return mRecords; - } - - /** - * Return the length of this NDEF Message if it is written to a byte array - * with {@link #toByteArray}.<p> - * An NDEF Message can be formatted to bytes in different ways - * depending on chunking, SR, and ID flags, so the length returned - * by this method may not be equal to the length of the original - * byte array used to construct this NDEF Message. However it will - * always be equal to the length of the byte array produced by - * {@link #toByteArray}. - * - * @return length of this NDEF Message when written to bytes with {@link #toByteArray} - * @see #toByteArray - */ - public int getByteArrayLength() { - int length = 0; - for (NdefRecord r : mRecords) { - length += r.getByteLength(); - } - return length; - } - - /** - * Return this NDEF Message as raw bytes.<p> - * The NDEF Message is formatted as per the NDEF 1.0 specification, - * and the byte array is suitable for network transmission or storage - * in an NFC Forum NDEF compatible tag.<p> - * This method will not chunk any records, and will always use the - * short record (SR) format and omit the identifier field when possible. - * - * @return NDEF Message in binary format - * @see #getByteArrayLength() - */ - public byte[] toByteArray() { - int length = getByteArrayLength(); - ByteBuffer buffer = ByteBuffer.allocate(length); - - for (int i=0; i<mRecords.length; i++) { - boolean mb = (i == 0); // first record - boolean me = (i == mRecords.length - 1); // last record - mRecords[i].writeToByteBuffer(buffer, mb, me); - } - - return buffer.array(); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mRecords.length); - dest.writeTypedArray(mRecords, flags); - } - - public static final @android.annotation.NonNull Parcelable.Creator<NdefMessage> CREATOR = - new Parcelable.Creator<NdefMessage>() { - @Override - public NdefMessage createFromParcel(Parcel in) { - int recordsLength = in.readInt(); - NdefRecord[] records = new NdefRecord[recordsLength]; - in.readTypedArray(records, NdefRecord.CREATOR); - return new NdefMessage(records); - } - @Override - public NdefMessage[] newArray(int size) { - return new NdefMessage[size]; - } - }; - - @Override - public int hashCode() { - return Arrays.hashCode(mRecords); - } - - /** - * Returns true if the specified NDEF Message contains - * identical NDEF Records. - */ - @Override - public boolean equals(@Nullable Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - NdefMessage other = (NdefMessage) obj; - return Arrays.equals(mRecords, other.mRecords); - } - - @Override - public String toString() { - return "NdefMessage " + Arrays.toString(mRecords); - } - - /** - * Dump debugging information as a NdefMessageProto - * @hide - * - * Note: - * See proto definition in frameworks/base/core/proto/android/nfc/ndef.proto - * When writing a nested message, must call {@link ProtoOutputStream#start(long)} before and - * {@link ProtoOutputStream#end(long)} after. - * Never reuse a proto field number. When removing a field, mark it as reserved. - */ - public void dumpDebug(ProtoOutputStream proto) { - for (NdefRecord record : mRecords) { - long token = proto.start(NdefMessageProto.NDEF_RECORDS); - record.dumpDebug(proto); - proto.end(token); - } - } -}
\ No newline at end of file diff --git a/nfc/java/android/nfc/NdefRecord.aidl b/nfc/java/android/nfc/NdefRecord.aidl deleted file mode 100644 index 10f89d0936e4..000000000000 --- a/nfc/java/android/nfc/NdefRecord.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2010 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; - -parcelable NdefRecord;
\ No newline at end of file diff --git a/nfc/java/android/nfc/NdefRecord.java b/nfc/java/android/nfc/NdefRecord.java deleted file mode 100644 index 7bf4355d5b35..000000000000 --- a/nfc/java/android/nfc/NdefRecord.java +++ /dev/null @@ -1,1080 +0,0 @@ -/* - * Copyright (C) 2010 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.Nullable; -import android.compat.annotation.UnsupportedAppUsage; -import android.content.Intent; -import android.net.Uri; -import android.os.Build; -import android.os.Parcel; -import android.os.Parcelable; -import android.util.proto.ProtoOutputStream; - -import java.nio.BufferUnderflowException; -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Locale; - -/** - * Represents an immutable NDEF Record. - * <p> - * NDEF (NFC Data Exchange Format) is a light-weight binary format, - * used to encapsulate typed data. It is specified by the NFC Forum, - * for transmission and storage with NFC, however it is transport agnostic. - * <p> - * NDEF defines messages and records. An NDEF Record contains - * typed data, such as MIME-type media, a URI, or a custom - * application payload. An NDEF Message is a container for - * one or more NDEF Records. - * <p> - * This class represents logical (complete) NDEF Records, and can not be - * used to represent chunked (partial) NDEF Records. However - * {@link NdefMessage#NdefMessage(byte[])} can be used to parse a message - * containing chunked records, and will return a message with unchunked - * (complete) records. - * <p> - * A logical NDEF Record always contains a 3-bit TNF (Type Name Field) - * that provides high level typing for the rest of the record. The - * remaining fields are variable length and not always present: - * <ul> - * <li><em>type</em>: detailed typing for the payload</li> - * <li><em>id</em>: identifier meta-data, not commonly used</li> - * <li><em>payload</em>: the actual payload</li> - * </ul> - * <p> - * Helpers such as {@link NdefRecord#createUri}, {@link NdefRecord#createMime} - * and {@link NdefRecord#createExternal} are included to create well-formatted - * NDEF Records with correctly set tnf, type, id and payload fields, please - * use these helpers whenever possible. - * <p> - * Use the constructor {@link #NdefRecord(short, byte[], byte[], byte[])} - * if you know what you are doing and what to set the fields individually. - * Only basic validation is performed with this constructor, so it is possible - * to create records that do not confirm to the strict NFC Forum - * specifications. - * <p> - * The binary representation of an NDEF Record includes additional flags to - * indicate location with an NDEF message, provide support for chunking of - * NDEF records, and to pack optional fields. This class does not expose - * those details. To write an NDEF Record as binary you must first put it - * into an {@link NdefMessage}, then call {@link NdefMessage#toByteArray()}. - * <p class="note"> - * {@link NdefMessage} and {@link NdefRecord} implementations are - * always available, even on Android devices that do not have NFC hardware. - * <p class="note"> - * {@link NdefRecord}s are intended to be immutable (and thread-safe), - * however they may contain mutable fields. So take care not to modify - * mutable fields passed into constructors, or modify mutable fields - * obtained by getter methods, unless such modification is explicitly - * marked as safe. - * - * @see NfcAdapter#ACTION_NDEF_DISCOVERED - * @see NdefMessage - */ -public final class NdefRecord implements Parcelable { - /** - * Indicates the record is empty.<p> - * Type, id and payload fields are empty in a {@literal TNF_EMPTY} record. - */ - public static final short TNF_EMPTY = 0x00; - - /** - * Indicates the type field contains a well-known RTD type name.<p> - * Use this tnf with RTD types such as {@link #RTD_TEXT}, {@link #RTD_URI}. - * <p> - * The RTD type name format is specified in NFCForum-TS-RTD_1.0. - * - * @see #RTD_URI - * @see #RTD_TEXT - * @see #RTD_SMART_POSTER - * @see #createUri - */ - public static final short TNF_WELL_KNOWN = 0x01; - - /** - * Indicates the type field contains a media-type BNF - * construct, defined by RFC 2046.<p> - * Use this with MIME type names such as {@literal "image/jpeg"}, or - * using the helper {@link #createMime}. - * - * @see #createMime - */ - public static final short TNF_MIME_MEDIA = 0x02; - - /** - * Indicates the type field contains an absolute-URI - * BNF construct defined by RFC 3986.<p> - * When creating new records prefer {@link #createUri}, - * since it offers more compact URI encoding - * ({@literal #RTD_URI} allows compression of common URI prefixes). - * - * @see #createUri - */ - public static final short TNF_ABSOLUTE_URI = 0x03; - - /** - * Indicates the type field contains an external type name.<p> - * Used to encode custom payloads. When creating new records - * use the helper {@link #createExternal}.<p> - * The external-type RTD format is specified in NFCForum-TS-RTD_1.0.<p> - * <p> - * Note this TNF should not be used with RTD_TEXT or RTD_URI constants. - * Those are well known RTD constants, not external RTD constants. - * - * @see #createExternal - */ - public static final short TNF_EXTERNAL_TYPE = 0x04; - - /** - * Indicates the payload type is unknown.<p> - * NFC Forum explains this should be treated similarly to the - * "application/octet-stream" MIME type. The payload - * type is not explicitly encoded within the record. - * <p> - * The type field is empty in an {@literal TNF_UNKNOWN} record. - */ - public static final short TNF_UNKNOWN = 0x05; - - /** - * Indicates the payload is an intermediate or final chunk of a chunked - * NDEF Record.<p> - * {@literal TNF_UNCHANGED} can not be used with this class - * since all {@link NdefRecord}s are already unchunked, however they - * may appear in the binary format. - */ - public static final short TNF_UNCHANGED = 0x06; - - /** - * Reserved TNF type. - * <p> - * The NFC Forum NDEF Specification v1.0 suggests for NDEF parsers to treat this - * value like TNF_UNKNOWN. - * @hide - */ - public static final short TNF_RESERVED = 0x07; - - /** - * RTD Text type. For use with {@literal TNF_WELL_KNOWN}. - * @see #TNF_WELL_KNOWN - */ - public static final byte[] RTD_TEXT = {0x54}; // "T" - - /** - * RTD URI type. For use with {@literal TNF_WELL_KNOWN}. - * @see #TNF_WELL_KNOWN - */ - public static final byte[] RTD_URI = {0x55}; // "U" - - /** - * RTD Smart Poster type. For use with {@literal TNF_WELL_KNOWN}. - * @see #TNF_WELL_KNOWN - */ - public static final byte[] RTD_SMART_POSTER = {0x53, 0x70}; // "Sp" - - /** - * RTD Alternative Carrier type. For use with {@literal TNF_WELL_KNOWN}. - * @see #TNF_WELL_KNOWN - */ - public static final byte[] RTD_ALTERNATIVE_CARRIER = {0x61, 0x63}; // "ac" - - /** - * RTD Handover Carrier type. For use with {@literal TNF_WELL_KNOWN}. - * @see #TNF_WELL_KNOWN - */ - public static final byte[] RTD_HANDOVER_CARRIER = {0x48, 0x63}; // "Hc" - - /** - * RTD Handover Request type. For use with {@literal TNF_WELL_KNOWN}. - * @see #TNF_WELL_KNOWN - */ - public static final byte[] RTD_HANDOVER_REQUEST = {0x48, 0x72}; // "Hr" - - /** - * RTD Handover Select type. For use with {@literal TNF_WELL_KNOWN}. - * @see #TNF_WELL_KNOWN - */ - public static final byte[] RTD_HANDOVER_SELECT = {0x48, 0x73}; // "Hs" - - /** - * RTD Android app type. For use with {@literal TNF_EXTERNAL}. - * <p> - * The payload of a record with type RTD_ANDROID_APP - * should be the package name identifying an application. - * Multiple RTD_ANDROID_APP records may be included - * in a single {@link NdefMessage}. - * <p> - * Use {@link #createApplicationRecord(String)} to create - * RTD_ANDROID_APP records. - * @hide - */ - public static final byte[] RTD_ANDROID_APP = "android.com:pkg".getBytes(); - - private static final byte FLAG_MB = (byte) 0x80; - private static final byte FLAG_ME = (byte) 0x40; - private static final byte FLAG_CF = (byte) 0x20; - private static final byte FLAG_SR = (byte) 0x10; - private static final byte FLAG_IL = (byte) 0x08; - - /** - * NFC Forum "URI Record Type Definition"<p> - * This is a mapping of "URI Identifier Codes" to URI string prefixes, - * per section 3.2.2 of the NFC Forum URI Record Type Definition document. - */ - private static final String[] URI_PREFIX_MAP = new String[] { - "", // 0x00 - "http://www.", // 0x01 - "https://www.", // 0x02 - "http://", // 0x03 - "https://", // 0x04 - "tel:", // 0x05 - "mailto:", // 0x06 - "ftp://anonymous:anonymous@", // 0x07 - "ftp://ftp.", // 0x08 - "ftps://", // 0x09 - "sftp://", // 0x0A - "smb://", // 0x0B - "nfs://", // 0x0C - "ftp://", // 0x0D - "dav://", // 0x0E - "news:", // 0x0F - "telnet://", // 0x10 - "imap:", // 0x11 - "rtsp://", // 0x12 - "urn:", // 0x13 - "pop:", // 0x14 - "sip:", // 0x15 - "sips:", // 0x16 - "tftp:", // 0x17 - "btspp://", // 0x18 - "btl2cap://", // 0x19 - "btgoep://", // 0x1A - "tcpobex://", // 0x1B - "irdaobex://", // 0x1C - "file://", // 0x1D - "urn:epc:id:", // 0x1E - "urn:epc:tag:", // 0x1F - "urn:epc:pat:", // 0x20 - "urn:epc:raw:", // 0x21 - "urn:epc:", // 0x22 - "urn:nfc:", // 0x23 - }; - - private static final int MAX_PAYLOAD_SIZE = 10 * (1 << 20); // 10 MB payload limit - - private static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; - - private final short mTnf; - private final byte[] mType; - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - private final byte[] mId; - private final byte[] mPayload; - - /** - * Create a new Android Application Record (AAR). - * <p> - * This record indicates to other Android devices the package - * that should be used to handle the entire NDEF message. - * You can embed this record anywhere into your message - * to ensure that the intended package receives the message. - * <p> - * When an Android device dispatches an {@link NdefMessage} - * containing one or more Android application records, - * the applications contained in those records will be the - * preferred target for the {@link NfcAdapter#ACTION_NDEF_DISCOVERED} - * intent, in the order in which they appear in the message. - * This dispatch behavior was first added to Android in - * Ice Cream Sandwich. - * <p> - * If none of the applications have a are installed on the device, - * a Market link will be opened to the first application. - * <p> - * Note that Android application records do not overrule - * applications that have called - * {@link NfcAdapter#enableForegroundDispatch}. - * - * @param packageName Android package name - * @return Android application NDEF record - */ - public static NdefRecord createApplicationRecord(String packageName) { - if (packageName == null) throw new NullPointerException("packageName is null"); - if (packageName.length() == 0) throw new IllegalArgumentException("packageName is empty"); - - return new NdefRecord(TNF_EXTERNAL_TYPE, RTD_ANDROID_APP, null, - packageName.getBytes(StandardCharsets.UTF_8)); - } - - /** - * Create a new NDEF Record containing a URI.<p> - * Use this method to encode a URI (or URL) into an NDEF Record.<p> - * Uses the well known URI type representation: {@link #TNF_WELL_KNOWN} - * and {@link #RTD_URI}. This is the most efficient encoding - * of a URI into NDEF.<p> - * The uri parameter will be normalized with - * {@link Uri#normalizeScheme} to set the scheme to lower case to - * follow Android best practices for intent filtering. - * However the unchecked exception - * {@link IllegalArgumentException} may be thrown if the uri - * parameter has serious problems, for example if it is empty, so always - * catch this exception if you are passing user-generated data into this - * method.<p> - * - * Reference specification: NFCForum-TS-RTD_URI_1.0 - * - * @param uri URI to encode. - * @return an NDEF Record containing the URI - * @throws IllegalArugmentException if the uri is empty or invalid - */ - public static NdefRecord createUri(Uri uri) { - if (uri == null) throw new NullPointerException("uri is null"); - - uri = uri.normalizeScheme(); - String uriString = uri.toString(); - if (uriString.length() == 0) throw new IllegalArgumentException("uri is empty"); - - byte prefix = 0; - for (int i = 1; i < URI_PREFIX_MAP.length; i++) { - if (uriString.startsWith(URI_PREFIX_MAP[i])) { - prefix = (byte) i; - uriString = uriString.substring(URI_PREFIX_MAP[i].length()); - break; - } - } - byte[] uriBytes = uriString.getBytes(StandardCharsets.UTF_8); - byte[] recordBytes = new byte[uriBytes.length + 1]; - recordBytes[0] = prefix; - System.arraycopy(uriBytes, 0, recordBytes, 1, uriBytes.length); - return new NdefRecord(TNF_WELL_KNOWN, RTD_URI, null, recordBytes); - } - - /** - * Create a new NDEF Record containing a URI.<p> - * Use this method to encode a URI (or URL) into an NDEF Record.<p> - * Uses the well known URI type representation: {@link #TNF_WELL_KNOWN} - * and {@link #RTD_URI}. This is the most efficient encoding - * of a URI into NDEF.<p> - * The uriString parameter will be normalized with - * {@link Uri#normalizeScheme} to set the scheme to lower case to - * follow Android best practices for intent filtering. - * However the unchecked exception - * {@link IllegalArgumentException} may be thrown if the uriString - * parameter has serious problems, for example if it is empty, so always - * catch this exception if you are passing user-generated data into this - * method.<p> - * - * Reference specification: NFCForum-TS-RTD_URI_1.0 - * - * @param uriString string URI to encode. - * @return an NDEF Record containing the URI - * @throws IllegalArugmentException if the uriString is empty or invalid - */ - public static NdefRecord createUri(String uriString) { - return createUri(Uri.parse(uriString)); - } - - /** - * Create a new NDEF Record containing MIME data.<p> - * Use this method to encode MIME-typed data into an NDEF Record, - * such as "text/plain", or "image/jpeg".<p> - * The mimeType parameter will be normalized with - * {@link Intent#normalizeMimeType} to follow Android best - * practices for intent filtering, for example to force lower-case. - * However the unchecked exception - * {@link IllegalArgumentException} may be thrown - * if the mimeType parameter has serious problems, - * for example if it is empty, so always catch this - * exception if you are passing user-generated data into this method. - * <p> - * For efficiency, This method might not make an internal copy of the - * mimeData byte array, so take care not - * to modify the mimeData byte array while still using the returned - * NdefRecord. - * - * @param mimeType a valid MIME type - * @param mimeData MIME data as bytes - * @return an NDEF Record containing the MIME-typed data - * @throws IllegalArugmentException if the mimeType is empty or invalid - * - */ - public static NdefRecord createMime(String mimeType, byte[] mimeData) { - if (mimeType == null) throw new NullPointerException("mimeType is null"); - - // We only do basic MIME type validation: trying to follow the - // RFCs strictly only ends in tears, since there are lots of MIME - // types in common use that are not strictly valid as per RFC rules - mimeType = Intent.normalizeMimeType(mimeType); - if (mimeType.length() == 0) throw new IllegalArgumentException("mimeType is empty"); - int slashIndex = mimeType.indexOf('/'); - if (slashIndex == 0) throw new IllegalArgumentException("mimeType must have major type"); - if (slashIndex == mimeType.length() - 1) { - throw new IllegalArgumentException("mimeType must have minor type"); - } - // missing '/' is allowed - - // MIME RFCs suggest ASCII encoding for content-type - byte[] typeBytes = mimeType.getBytes(StandardCharsets.US_ASCII); - return new NdefRecord(TNF_MIME_MEDIA, typeBytes, null, mimeData); - } - - /** - * Create a new NDEF Record containing external (application-specific) data.<p> - * Use this method to encode application specific data into an NDEF Record. - * The data is typed by a domain name (usually your Android package name) and - * a domain-specific type. This data is packaged into a "NFC Forum External - * Type" NDEF Record.<p> - * NFC Forum requires that the domain and type used in an external record - * are treated as case insensitive, however Android intent filtering is - * always case sensitive. So this method will force the domain and type to - * lower-case before creating the NDEF Record.<p> - * The unchecked exception {@link IllegalArgumentException} will be thrown - * if the domain and type have serious problems, for example if either field - * is empty, so always catch this - * exception if you are passing user-generated data into this method.<p> - * There are no such restrictions on the payload data.<p> - * For efficiency, This method might not make an internal copy of the - * data byte array, so take care not - * to modify the data byte array while still using the returned - * NdefRecord. - * - * Reference specification: NFCForum-TS-RTD_1.0 - * @param domain domain-name of issuing organization - * @param type domain-specific type of data - * @param data payload as bytes - * @throws IllegalArugmentException if either domain or type are empty or invalid - */ - public static NdefRecord createExternal(String domain, String type, byte[] data) { - if (domain == null) throw new NullPointerException("domain is null"); - if (type == null) throw new NullPointerException("type is null"); - - domain = domain.trim().toLowerCase(Locale.ROOT); - type = type.trim().toLowerCase(Locale.ROOT); - - if (domain.length() == 0) throw new IllegalArgumentException("domain is empty"); - if (type.length() == 0) throw new IllegalArgumentException("type is empty"); - - byte[] byteDomain = domain.getBytes(StandardCharsets.UTF_8); - byte[] byteType = type.getBytes(StandardCharsets.UTF_8); - byte[] b = new byte[byteDomain.length + 1 + byteType.length]; - System.arraycopy(byteDomain, 0, b, 0, byteDomain.length); - b[byteDomain.length] = ':'; - System.arraycopy(byteType, 0, b, byteDomain.length + 1, byteType.length); - - return new NdefRecord(TNF_EXTERNAL_TYPE, b, null, data); - } - - /** - * Create a new NDEF record containing UTF-8 text data.<p> - * - * The caller can either specify the language code for the provided text, - * or otherwise the language code corresponding to the current default - * locale will be used. - * - * Reference specification: NFCForum-TS-RTD_Text_1.0 - * @param languageCode The languageCode for the record. If locale is empty or null, - * the language code of the current default locale will be used. - * @param text The text to be encoded in the record. Will be represented in UTF-8 format. - * @throws IllegalArgumentException if text is null - */ - public static NdefRecord createTextRecord(String languageCode, String text) { - if (text == null) throw new NullPointerException("text is null"); - - byte[] textBytes = text.getBytes(StandardCharsets.UTF_8); - - byte[] languageCodeBytes = null; - if (languageCode != null && !languageCode.isEmpty()) { - languageCodeBytes = languageCode.getBytes(StandardCharsets.US_ASCII); - } else { - languageCodeBytes = Locale.getDefault().getLanguage(). - getBytes(StandardCharsets.US_ASCII); - } - // We only have 6 bits to indicate ISO/IANA language code. - if (languageCodeBytes.length >= 64) { - throw new IllegalArgumentException("language code is too long, must be <64 bytes."); - } - ByteBuffer buffer = ByteBuffer.allocate(1 + languageCodeBytes.length + textBytes.length); - - byte status = (byte) (languageCodeBytes.length & 0xFF); - buffer.put(status); - buffer.put(languageCodeBytes); - buffer.put(textBytes); - - return new NdefRecord(TNF_WELL_KNOWN, RTD_TEXT, null, buffer.array()); - } - - /** - * Construct an NDEF Record from its component fields.<p> - * Recommend to use helpers such as {#createUri} or - * {{@link #createExternal} where possible, since they perform - * stricter validation that the record is correctly formatted - * as per NDEF specifications. However if you know what you are - * doing then this constructor offers the most flexibility.<p> - * An {@link NdefRecord} represents a logical (complete) - * record, and cannot represent NDEF Record chunks.<p> - * Basic validation of the tnf, type, id and payload is performed - * as per the following rules: - * <ul> - * <li>The tnf paramter must be a 3-bit value.</li> - * <li>Records with a tnf of {@link #TNF_EMPTY} cannot have a type, - * id or payload.</li> - * <li>Records with a tnf of {@link #TNF_UNKNOWN} or {@literal 0x07} - * cannot have a type.</li> - * <li>Records with a tnf of {@link #TNF_UNCHANGED} are not allowed - * since this class only represents complete (unchunked) records.</li> - * </ul> - * This minimal validation is specified by - * NFCForum-TS-NDEF_1.0 section 3.2.6 (Type Name Format).<p> - * If any of the above validation - * steps fail then {@link IllegalArgumentException} is thrown.<p> - * Deep inspection of the type, id and payload fields is not - * performed, so it is possible to create NDEF Records - * that conform to section 3.2.6 - * but fail other more strict NDEF specification requirements. For - * example, the payload may be invalid given the tnf and type. - * <p> - * To omit a type, id or payload field, set the parameter to an - * empty byte array or null. - * - * @param tnf a 3-bit TNF constant - * @param type byte array, containing zero to 255 bytes, or null - * @param id byte array, containing zero to 255 bytes, or null - * @param payload byte array, containing zero to (2 ** 32 - 1) bytes, - * or null - * @throws IllegalArugmentException if a valid record cannot be created - */ - public NdefRecord(short tnf, byte[] type, byte[] id, byte[] payload) { - /* convert nulls */ - if (type == null) type = EMPTY_BYTE_ARRAY; - if (id == null) id = EMPTY_BYTE_ARRAY; - if (payload == null) payload = EMPTY_BYTE_ARRAY; - - String message = validateTnf(tnf, type, id, payload); - if (message != null) { - throw new IllegalArgumentException(message); - } - - mTnf = tnf; - mType = type; - mId = id; - mPayload = payload; - } - - /** - * Construct an NDEF Record from raw bytes.<p> - * This method is deprecated, use {@link NdefMessage#NdefMessage(byte[])} - * instead. This is because it does not make sense to parse a record: - * the NDEF binary format is only defined for a message, and the - * record flags MB and ME do not make sense outside of the context of - * an entire message.<p> - * This implementation will attempt to parse a single record by ignoring - * the MB and ME flags, and otherwise following the rules of - * {@link NdefMessage#NdefMessage(byte[])}.<p> - * - * @param data raw bytes to parse - * @throws FormatException if the data cannot be parsed into a valid record - * @deprecated use {@link NdefMessage#NdefMessage(byte[])} instead. - */ - @Deprecated - public NdefRecord(byte[] data) throws FormatException { - ByteBuffer buffer = ByteBuffer.wrap(data); - NdefRecord[] rs = parse(buffer, true); - - if (buffer.remaining() > 0) { - throw new FormatException("data too long"); - } - - mTnf = rs[0].mTnf; - mType = rs[0].mType; - mId = rs[0].mId; - mPayload = rs[0].mPayload; - } - - /** - * Returns the 3-bit TNF. - * <p> - * TNF is the top-level type. - */ - public short getTnf() { - return mTnf; - } - - /** - * Returns the variable length Type field. - * <p> - * This should be used in conjunction with the TNF field to determine the - * payload format. - * <p> - * Returns an empty byte array if this record - * does not have a type field. - */ - public byte[] getType() { - return mType.clone(); - } - - /** - * Returns the variable length ID. - * <p> - * Returns an empty byte array if this record - * does not have an id field. - */ - public byte[] getId() { - return mId.clone(); - } - - /** - * Returns the variable length payload. - * <p> - * Returns an empty byte array if this record - * does not have a payload field. - */ - public byte[] getPayload() { - return mPayload.clone(); - } - - /** - * Return this NDEF Record as a byte array.<p> - * This method is deprecated, use {@link NdefMessage#toByteArray} - * instead. This is because the NDEF binary format is not defined for - * a record outside of the context of a message: the MB and ME flags - * cannot be set without knowing the location inside a message.<p> - * This implementation will attempt to serialize a single record by - * always setting the MB and ME flags (in other words, assume this - * is a single-record NDEF Message).<p> - * - * @deprecated use {@link NdefMessage#toByteArray()} instead - */ - @Deprecated - public byte[] toByteArray() { - ByteBuffer buffer = ByteBuffer.allocate(getByteLength()); - writeToByteBuffer(buffer, true, true); - return buffer.array(); - } - - /** - * Map this record to a MIME type, or return null if it cannot be mapped.<p> - * Currently this method considers all {@link #TNF_MIME_MEDIA} records to - * be MIME records, as well as some {@link #TNF_WELL_KNOWN} records such as - * {@link #RTD_TEXT}. If this is a MIME record then the MIME type as string - * is returned, otherwise null is returned.<p> - * This method does not perform validation that the MIME type is - * actually valid. It always attempts to - * return a string containing the type if this is a MIME record.<p> - * The returned MIME type will by normalized to lower-case using - * {@link Intent#normalizeMimeType}.<p> - * The MIME payload can be obtained using {@link #getPayload}. - * - * @return MIME type as a string, or null if this is not a MIME record - */ - public String toMimeType() { - switch (mTnf) { - case NdefRecord.TNF_WELL_KNOWN: - if (Arrays.equals(mType, NdefRecord.RTD_TEXT)) { - return "text/plain"; - } - break; - case NdefRecord.TNF_MIME_MEDIA: - String mimeType = new String(mType, StandardCharsets.US_ASCII); - return Intent.normalizeMimeType(mimeType); - } - return null; - } - - /** - * Map this record to a URI, or return null if it cannot be mapped.<p> - * Currently this method considers the following to be URI records: - * <ul> - * <li>{@link #TNF_ABSOLUTE_URI} records.</li> - * <li>{@link #TNF_WELL_KNOWN} with a type of {@link #RTD_URI}.</li> - * <li>{@link #TNF_WELL_KNOWN} with a type of {@link #RTD_SMART_POSTER} - * and containing a URI record in the NDEF message nested in the payload. - * </li> - * <li>{@link #TNF_EXTERNAL_TYPE} records.</li> - * </ul> - * If this is not a URI record by the above rules, then null is returned.<p> - * This method does not perform validation that the URI is - * actually valid: it always attempts to create and return a URI if - * this record appears to be a URI record by the above rules.<p> - * The returned URI will be normalized to have a lower case scheme - * using {@link Uri#normalizeScheme}.<p> - * - * @return URI, or null if this is not a URI record - */ - public Uri toUri() { - return toUri(false); - } - - private Uri toUri(boolean inSmartPoster) { - switch (mTnf) { - case TNF_WELL_KNOWN: - if (Arrays.equals(mType, RTD_SMART_POSTER) && !inSmartPoster) { - try { - // check payload for a nested NDEF Message containing a URI - NdefMessage nestedMessage = new NdefMessage(mPayload); - for (NdefRecord nestedRecord : nestedMessage.getRecords()) { - Uri uri = nestedRecord.toUri(true); - if (uri != null) { - return uri; - } - } - } catch (FormatException e) { } - } else if (Arrays.equals(mType, RTD_URI)) { - Uri wktUri = parseWktUri(); - return (wktUri != null ? wktUri.normalizeScheme() : null); - } - break; - - case TNF_ABSOLUTE_URI: - Uri uri = Uri.parse(new String(mType, StandardCharsets.UTF_8)); - return uri.normalizeScheme(); - - case TNF_EXTERNAL_TYPE: - if (inSmartPoster) { - break; - } - return Uri.parse("vnd.android.nfc://ext/" + new String(mType, StandardCharsets.US_ASCII)); - } - return null; - } - - /** - * Return complete URI of {@link #TNF_WELL_KNOWN}, {@link #RTD_URI} records. - * @return complete URI, or null if invalid - */ - private Uri parseWktUri() { - if (mPayload.length < 2) { - return null; - } - - // payload[0] contains the URI Identifier Code, as per - // NFC Forum "URI Record Type Definition" section 3.2.2. - int prefixIndex = (mPayload[0] & (byte)0xFF); - if (prefixIndex < 0 || prefixIndex >= URI_PREFIX_MAP.length) { - return null; - } - String prefix = URI_PREFIX_MAP[prefixIndex]; - String suffix = new String(Arrays.copyOfRange(mPayload, 1, mPayload.length), - StandardCharsets.UTF_8); - return Uri.parse(prefix + suffix); - } - - /** - * Main record parsing method.<p> - * Expects NdefMessage to begin immediately, allows trailing data.<p> - * Currently has strict validation of all fields as per NDEF 1.0 - * specification section 2.5. We will attempt to keep this as strict as - * possible to encourage well-formatted NDEF.<p> - * Always returns 1 or more NdefRecord's, or throws FormatException. - * - * @param buffer ByteBuffer to read from - * @param ignoreMbMe ignore MB and ME flags, and read only 1 complete record - * @return one or more records - * @throws FormatException on any parsing error - */ - static NdefRecord[] parse(ByteBuffer buffer, boolean ignoreMbMe) throws FormatException { - List<NdefRecord> records = new ArrayList<NdefRecord>(); - - try { - byte[] type = null; - byte[] id = null; - byte[] payload = null; - ArrayList<byte[]> chunks = new ArrayList<byte[]>(); - boolean inChunk = false; - short chunkTnf = -1; - boolean me = false; - - while (!me) { - byte flag = buffer.get(); - - boolean mb = (flag & NdefRecord.FLAG_MB) != 0; - me = (flag & NdefRecord.FLAG_ME) != 0; - boolean cf = (flag & NdefRecord.FLAG_CF) != 0; - boolean sr = (flag & NdefRecord.FLAG_SR) != 0; - boolean il = (flag & NdefRecord.FLAG_IL) != 0; - short tnf = (short)(flag & 0x07); - - if (!mb && records.size() == 0 && !inChunk && !ignoreMbMe) { - throw new FormatException("expected MB flag"); - } else if (mb && (records.size() != 0 || inChunk) && !ignoreMbMe) { - throw new FormatException("unexpected MB flag"); - } else if (inChunk && il) { - throw new FormatException("unexpected IL flag in non-leading chunk"); - } else if (cf && me) { - throw new FormatException("unexpected ME flag in non-trailing chunk"); - } else if (inChunk && tnf != NdefRecord.TNF_UNCHANGED) { - throw new FormatException("expected TNF_UNCHANGED in non-leading chunk"); - } else if (!inChunk && tnf == NdefRecord.TNF_UNCHANGED) { - throw new FormatException("" + - "unexpected TNF_UNCHANGED in first chunk or unchunked record"); - } - - int typeLength = buffer.get() & 0xFF; - long payloadLength = sr ? (buffer.get() & 0xFF) : (buffer.getInt() & 0xFFFFFFFFL); - int idLength = il ? (buffer.get() & 0xFF) : 0; - - if (inChunk && typeLength != 0) { - throw new FormatException("expected zero-length type in non-leading chunk"); - } - - if (!inChunk) { - type = (typeLength > 0 ? new byte[typeLength] : EMPTY_BYTE_ARRAY); - id = (idLength > 0 ? new byte[idLength] : EMPTY_BYTE_ARRAY); - buffer.get(type); - buffer.get(id); - } - - ensureSanePayloadSize(payloadLength); - payload = (payloadLength > 0 ? new byte[(int)payloadLength] : EMPTY_BYTE_ARRAY); - buffer.get(payload); - - if (cf && !inChunk) { - // first chunk - if (typeLength == 0 && tnf != NdefRecord.TNF_UNKNOWN) { - throw new FormatException("expected non-zero type length in first chunk"); - } - chunks.clear(); - chunkTnf = tnf; - } - if (cf || inChunk) { - // any chunk - chunks.add(payload); - } - if (!cf && inChunk) { - // last chunk, flatten the payload - payloadLength = 0; - for (byte[] p : chunks) { - payloadLength += p.length; - } - ensureSanePayloadSize(payloadLength); - payload = new byte[(int)payloadLength]; - int i = 0; - for (byte[] p : chunks) { - System.arraycopy(p, 0, payload, i, p.length); - i += p.length; - } - tnf = chunkTnf; - } - if (cf) { - // more chunks to come - inChunk = true; - continue; - } else { - inChunk = false; - } - - String error = validateTnf(tnf, type, id, payload); - if (error != null) { - throw new FormatException(error); - } - records.add(new NdefRecord(tnf, type, id, payload)); - if (ignoreMbMe) { // for parsing a single NdefRecord - break; - } - } - } catch (BufferUnderflowException e) { - throw new FormatException("expected more data", e); - } - return records.toArray(new NdefRecord[records.size()]); - } - - private static void ensureSanePayloadSize(long size) throws FormatException { - if (size > MAX_PAYLOAD_SIZE) { - throw new FormatException( - "payload above max limit: " + size + " > " + MAX_PAYLOAD_SIZE); - } - } - - /** - * Perform simple validation that the tnf is valid.<p> - * Validates the requirements of NFCForum-TS-NDEF_1.0 section - * 3.2.6 (Type Name Format). This just validates that the tnf - * is valid, and that the relevant type, id and payload - * fields are present (or empty) for this tnf. It does not - * perform any deep inspection of the type, id and payload fields.<p> - * Also does not allow TNF_UNCHANGED since this class is only used - * to present logical (unchunked) records. - * - * @return null if valid, or a string error if invalid. - */ - static String validateTnf(short tnf, byte[] type, byte[] id, byte[] payload) { - switch (tnf) { - case TNF_EMPTY: - if (type.length != 0 || id.length != 0 || payload.length != 0) { - return "unexpected data in TNF_EMPTY record"; - } - return null; - case TNF_WELL_KNOWN: - case TNF_MIME_MEDIA: - case TNF_ABSOLUTE_URI: - case TNF_EXTERNAL_TYPE: - return null; - case TNF_UNKNOWN: - case TNF_RESERVED: - if (type.length != 0) { - return "unexpected type field in TNF_UNKNOWN or TNF_RESERVEd record"; - } - return null; - case TNF_UNCHANGED: - return "unexpected TNF_UNCHANGED in first chunk or logical record"; - default: - return String.format("unexpected tnf value: 0x%02x", tnf); - } - } - - /** - * Serialize record for network transmission.<p> - * Uses specified MB and ME flags.<p> - * Does not chunk records. - */ - void writeToByteBuffer(ByteBuffer buffer, boolean mb, boolean me) { - boolean sr = mPayload.length < 256; - boolean il = mTnf == TNF_EMPTY ? true : mId.length > 0; - - byte flags = (byte)((mb ? FLAG_MB : 0) | (me ? FLAG_ME : 0) | - (sr ? FLAG_SR : 0) | (il ? FLAG_IL : 0) | mTnf); - buffer.put(flags); - - buffer.put((byte)mType.length); - if (sr) { - buffer.put((byte)mPayload.length); - } else { - buffer.putInt(mPayload.length); - } - if (il) { - buffer.put((byte)mId.length); - } - - buffer.put(mType); - buffer.put(mId); - buffer.put(mPayload); - } - - /** - * Get byte length of serialized record. - */ - int getByteLength() { - int length = 3 + mType.length + mId.length + mPayload.length; - - boolean sr = mPayload.length < 256; - boolean il = mTnf == TNF_EMPTY ? true : mId.length > 0; - - if (!sr) length += 3; - if (il) length += 1; - - return length; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mTnf); - dest.writeInt(mType.length); - dest.writeByteArray(mType); - dest.writeInt(mId.length); - dest.writeByteArray(mId); - dest.writeInt(mPayload.length); - dest.writeByteArray(mPayload); - } - - public static final @android.annotation.NonNull Parcelable.Creator<NdefRecord> CREATOR = - new Parcelable.Creator<NdefRecord>() { - @Override - public NdefRecord createFromParcel(Parcel in) { - short tnf = (short)in.readInt(); - int typeLength = in.readInt(); - byte[] type = new byte[typeLength]; - in.readByteArray(type); - int idLength = in.readInt(); - byte[] id = new byte[idLength]; - in.readByteArray(id); - int payloadLength = in.readInt(); - byte[] payload = new byte[payloadLength]; - in.readByteArray(payload); - - return new NdefRecord(tnf, type, id, payload); - } - @Override - public NdefRecord[] newArray(int size) { - return new NdefRecord[size]; - } - }; - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + Arrays.hashCode(mId); - result = prime * result + Arrays.hashCode(mPayload); - result = prime * result + mTnf; - result = prime * result + Arrays.hashCode(mType); - return result; - } - - /** - * Returns true if the specified NDEF Record contains - * identical tnf, type, id and payload fields. - */ - @Override - public boolean equals(@Nullable Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - NdefRecord other = (NdefRecord) obj; - if (!Arrays.equals(mId, other.mId)) return false; - if (!Arrays.equals(mPayload, other.mPayload)) return false; - if (mTnf != other.mTnf) return false; - return Arrays.equals(mType, other.mType); - } - - @Override - public String toString() { - StringBuilder b = new StringBuilder(String.format("NdefRecord tnf=%X", mTnf)); - if (mType.length > 0) b.append(" type=").append(bytesToString(mType)); - if (mId.length > 0) b.append(" id=").append(bytesToString(mId)); - if (mPayload.length > 0) b.append(" payload=").append(bytesToString(mPayload)); - return b.toString(); - } - - /** - * Dump debugging information as a NdefRecordProto - * @hide - * - * Note: - * See proto definition in frameworks/base/core/proto/android/nfc/ndef.proto - * When writing a nested message, must call {@link ProtoOutputStream#start(long)} before and - * {@link ProtoOutputStream#end(long)} after. - * Never reuse a proto field number. When removing a field, mark it as reserved. - */ - public void dumpDebug(ProtoOutputStream proto) { - proto.write(NdefRecordProto.TYPE, mType); - proto.write(NdefRecordProto.ID, mId); - proto.write(NdefRecordProto.PAYLOAD_BYTES, mPayload.length); - } - - private static StringBuilder bytesToString(byte[] bs) { - StringBuilder s = new StringBuilder(); - for (byte b : bs) { - s.append(String.format("%02X", b)); - } - return s; - } -} diff --git a/nfc/java/android/nfc/NfcActivityManager.java b/nfc/java/android/nfc/NfcActivityManager.java deleted file mode 100644 index 909eca7b0c9d..000000000000 --- a/nfc/java/android/nfc/NfcActivityManager.java +++ /dev/null @@ -1,403 +0,0 @@ -/* - * Copyright (C) 2011 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.app.Activity; -import android.app.Application; -import android.compat.annotation.UnsupportedAppUsage; -import android.nfc.NfcAdapter.ReaderCallback; -import android.os.Binder; -import android.os.Bundle; -import android.os.RemoteException; -import android.util.Log; - -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; - -/** - * Manages NFC API's that are coupled to the life-cycle of an Activity. - * - * <p>Uses {@link Application#registerActivityLifecycleCallbacks} to hook - * into activity life-cycle events such as onPause() and onResume(). - * - * @hide - */ -public final class NfcActivityManager extends IAppCallback.Stub - implements Application.ActivityLifecycleCallbacks { - static final String TAG = NfcAdapter.TAG; - static final Boolean DBG = false; - - @UnsupportedAppUsage - final NfcAdapter mAdapter; - - // All objects in the lists are protected by this - final List<NfcApplicationState> mApps; // Application(s) that have NFC state. Usually one - final List<NfcActivityState> mActivities; // Activities that have NFC state - - /** - * NFC State associated with an {@link Application}. - */ - class NfcApplicationState { - int refCount = 0; - final Application app; - public NfcApplicationState(Application app) { - this.app = app; - } - public void register() { - refCount++; - if (refCount == 1) { - this.app.registerActivityLifecycleCallbacks(NfcActivityManager.this); - } - } - public void unregister() { - refCount--; - if (refCount == 0) { - this.app.unregisterActivityLifecycleCallbacks(NfcActivityManager.this); - } else if (refCount < 0) { - Log.e(TAG, "-ve refcount for " + app); - } - } - } - - NfcApplicationState findAppState(Application app) { - for (NfcApplicationState appState : mApps) { - if (appState.app == app) { - return appState; - } - } - return null; - } - - void registerApplication(Application app) { - NfcApplicationState appState = findAppState(app); - if (appState == null) { - appState = new NfcApplicationState(app); - mApps.add(appState); - } - appState.register(); - } - - void unregisterApplication(Application app) { - NfcApplicationState appState = findAppState(app); - if (appState == null) { - Log.e(TAG, "app was not registered " + app); - return; - } - appState.unregister(); - } - - /** - * NFC state associated with an {@link Activity} - */ - class NfcActivityState { - boolean resumed = false; - Activity activity; - NfcAdapter.ReaderCallback readerCallback = null; - int readerModeFlags = 0; - Bundle readerModeExtras = null; - Binder token; - - int mPollTech = NfcAdapter.FLAG_USE_ALL_TECH; - int mListenTech = NfcAdapter.FLAG_USE_ALL_TECH; - - public NfcActivityState(Activity activity) { - if (activity.isDestroyed()) { - throw new IllegalStateException("activity is already destroyed"); - } - // Check if activity is resumed right now, as we will not - // immediately get a callback for that. - resumed = activity.isResumed(); - - this.activity = activity; - this.token = new Binder(); - registerApplication(activity.getApplication()); - } - public void destroy() { - unregisterApplication(activity.getApplication()); - resumed = false; - activity = null; - readerCallback = null; - readerModeFlags = 0; - readerModeExtras = null; - token = null; - - mPollTech = NfcAdapter.FLAG_USE_ALL_TECH; - mListenTech = NfcAdapter.FLAG_USE_ALL_TECH; - } - @Override - public String toString() { - StringBuilder s = new StringBuilder("["); - s.append(readerCallback); - s.append("]"); - return s.toString(); - } - } - - /** find activity state from mActivities */ - synchronized NfcActivityState findActivityState(Activity activity) { - for (NfcActivityState state : mActivities) { - if (state.activity == activity) { - return state; - } - } - return null; - } - - /** find or create activity state from mActivities */ - synchronized NfcActivityState getActivityState(Activity activity) { - NfcActivityState state = findActivityState(activity); - if (state == null) { - state = new NfcActivityState(activity); - mActivities.add(state); - } - return state; - } - - synchronized NfcActivityState findResumedActivityState() { - for (NfcActivityState state : mActivities) { - if (state.resumed) { - return state; - } - } - return null; - } - - synchronized void destroyActivityState(Activity activity) { - NfcActivityState activityState = findActivityState(activity); - if (activityState != null) { - activityState.destroy(); - mActivities.remove(activityState); - } - } - - public NfcActivityManager(NfcAdapter adapter) { - mAdapter = adapter; - mActivities = new LinkedList<NfcActivityState>(); - mApps = new ArrayList<NfcApplicationState>(1); // Android VM usually has 1 app - } - - public void enableReaderMode(Activity activity, ReaderCallback callback, int flags, - Bundle extras) { - boolean isResumed; - Binder token; - int pollTech, listenTech; - synchronized (NfcActivityManager.this) { - NfcActivityState state = getActivityState(activity); - state.readerCallback = callback; - state.readerModeFlags = flags; - state.readerModeExtras = extras; - pollTech = state.mPollTech; - listenTech = state.mListenTech; - token = state.token; - isResumed = state.resumed; - } - if (isResumed) { - if (listenTech != NfcAdapter.FLAG_USE_ALL_TECH - || pollTech != NfcAdapter.FLAG_USE_ALL_TECH) { - throw new IllegalStateException( - "Cannot be used when alternative DiscoveryTechnology is set"); - } else { - setReaderMode(token, flags, extras); - } - } - } - - public void disableReaderMode(Activity activity) { - boolean isResumed; - Binder token; - synchronized (NfcActivityManager.this) { - NfcActivityState state = getActivityState(activity); - state.readerCallback = null; - state.readerModeFlags = 0; - state.readerModeExtras = null; - token = state.token; - isResumed = state.resumed; - } - if (isResumed) { - setReaderMode(token, 0, null); - } - - } - - public void setReaderMode(Binder token, int flags, Bundle extras) { - if (DBG) Log.d(TAG, "Setting reader mode"); - NfcAdapter.callService(() -> NfcAdapter.sService.setReaderMode( - token, this, flags, extras, mAdapter.getContext().getPackageName())); - } - - /** - * Request or unrequest NFC service callbacks. - * Makes IPC call - do not hold lock. - */ - void requestNfcServiceCallback() { - NfcAdapter.callService(() -> NfcAdapter.sService.setAppCallback(this)); - } - - void verifyNfcPermission() { - NfcAdapter.callService(() -> NfcAdapter.sService.verifyNfcPermission()); - } - - @Override - public void onTagDiscovered(Tag tag) throws RemoteException { - NfcAdapter.ReaderCallback callback; - synchronized (NfcActivityManager.this) { - NfcActivityState state = findResumedActivityState(); - if (state == null) return; - - callback = state.readerCallback; - } - - // Make callback without lock - if (callback != null) { - callback.onTagDiscovered(tag); - } - - } - /** Callback from Activity life-cycle, on main thread */ - @Override - public void onActivityCreated(Activity activity, Bundle savedInstanceState) { /* NO-OP */ } - - /** Callback from Activity life-cycle, on main thread */ - @Override - public void onActivityStarted(Activity activity) { /* NO-OP */ } - - /** Callback from Activity life-cycle, on main thread */ - @Override - public void onActivityResumed(Activity activity) { - int readerModeFlags = 0; - Bundle readerModeExtras = null; - Binder token; - int pollTech; - int listenTech; - - synchronized (NfcActivityManager.this) { - NfcActivityState state = findActivityState(activity); - if (DBG) Log.d(TAG, "onResume() for " + activity + " " + state); - if (state == null) return; - state.resumed = true; - token = state.token; - readerModeFlags = state.readerModeFlags; - readerModeExtras = state.readerModeExtras; - - pollTech = state.mPollTech; - listenTech = state.mListenTech; - } - if (readerModeFlags != 0) { - setReaderMode(token, readerModeFlags, readerModeExtras); - } else if (listenTech != NfcAdapter.FLAG_USE_ALL_TECH - || pollTech != NfcAdapter.FLAG_USE_ALL_TECH) { - changeDiscoveryTech(token, pollTech, listenTech); - } - requestNfcServiceCallback(); - } - - /** Callback from Activity life-cycle, on main thread */ - @Override - public void onActivityPaused(Activity activity) { - boolean readerModeFlagsSet; - Binder token; - int pollTech; - int listenTech; - - synchronized (NfcActivityManager.this) { - NfcActivityState state = findActivityState(activity); - if (DBG) Log.d(TAG, "onPause() for " + activity + " " + state); - if (state == null) return; - state.resumed = false; - token = state.token; - readerModeFlagsSet = state.readerModeFlags != 0; - - pollTech = state.mPollTech; - listenTech = state.mListenTech; - } - if (readerModeFlagsSet) { - // Restore default p2p modes - setReaderMode(token, 0, null); - } else if (listenTech != NfcAdapter.FLAG_USE_ALL_TECH - || pollTech != NfcAdapter.FLAG_USE_ALL_TECH) { - changeDiscoveryTech(token, - NfcAdapter.FLAG_USE_ALL_TECH, NfcAdapter.FLAG_USE_ALL_TECH); - } - } - - /** Callback from Activity life-cycle, on main thread */ - @Override - public void onActivityStopped(Activity activity) { /* NO-OP */ } - - /** Callback from Activity life-cycle, on main thread */ - @Override - public void onActivitySaveInstanceState(Activity activity, Bundle outState) { /* NO-OP */ } - - /** Callback from Activity life-cycle, on main thread */ - @Override - public void onActivityDestroyed(Activity activity) { - synchronized (NfcActivityManager.this) { - NfcActivityState state = findActivityState(activity); - if (DBG) Log.d(TAG, "onDestroy() for " + activity + " " + state); - if (state != null) { - // release all associated references - destroyActivityState(activity); - } - } - } - - /** setDiscoveryTechnology() implementation */ - public void setDiscoveryTech(Activity activity, int pollTech, int listenTech) { - boolean isResumed; - Binder token; - boolean readerModeFlagsSet; - synchronized (NfcActivityManager.this) { - NfcActivityState state = getActivityState(activity); - readerModeFlagsSet = state.readerModeFlags != 0; - state.mListenTech = listenTech; - state.mPollTech = pollTech; - token = state.token; - isResumed = state.resumed; - } - if (!readerModeFlagsSet && isResumed) { - changeDiscoveryTech(token, pollTech, listenTech); - } else if (readerModeFlagsSet) { - throw new IllegalStateException("Cannot be used when the Reader Mode is enabled"); - } - } - - /** resetDiscoveryTechnology() implementation */ - public void resetDiscoveryTech(Activity activity) { - boolean isResumed; - Binder token; - boolean readerModeFlagsSet; - synchronized (NfcActivityManager.this) { - NfcActivityState state = getActivityState(activity); - state.mListenTech = NfcAdapter.FLAG_USE_ALL_TECH; - state.mPollTech = NfcAdapter.FLAG_USE_ALL_TECH; - token = state.token; - isResumed = state.resumed; - } - if (isResumed) { - changeDiscoveryTech(token, NfcAdapter.FLAG_USE_ALL_TECH, NfcAdapter.FLAG_USE_ALL_TECH); - } - - } - - private void changeDiscoveryTech(Binder token, int pollTech, int listenTech) { - NfcAdapter.callService( - () -> NfcAdapter.sService.updateDiscoveryTechnology( - token, pollTech, listenTech, mAdapter.getContext().getPackageName())); - } - -} diff --git a/nfc/java/android/nfc/NfcAdapter.java b/nfc/java/android/nfc/NfcAdapter.java deleted file mode 100644 index 63397c21b036..000000000000 --- a/nfc/java/android/nfc/NfcAdapter.java +++ /dev/null @@ -1,2949 +0,0 @@ -/* - * Copyright (C) 2010 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.Manifest; -import android.annotation.CallbackExecutor; -import android.annotation.FlaggedApi; -import android.annotation.IntDef; -import android.annotation.IntRange; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.RequiresPermission; -import android.annotation.SdkConstant; -import android.annotation.SdkConstant.SdkConstantType; -import android.annotation.SuppressLint; -import android.annotation.SystemApi; -import android.annotation.TestApi; -import android.annotation.UserIdInt; -import android.app.Activity; -import android.app.PendingIntent; -import android.compat.annotation.UnsupportedAppUsage; -import android.content.Context; -import android.content.IntentFilter; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.net.Uri; -import android.nfc.cardemulation.PollingFrame; -import android.nfc.tech.MifareClassic; -import android.nfc.tech.Ndef; -import android.nfc.tech.NfcA; -import android.nfc.tech.NfcF; -import android.os.Binder; -import android.os.Build; -import android.os.Bundle; -import android.os.Handler; -import android.os.IBinder; -import android.os.RemoteException; -import android.os.UserHandle; -import android.util.Log; - -import java.io.IOException; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.Executor; - -/** - * Represents the local NFC adapter. - * <p> - * Use the helper {@link #getDefaultAdapter(Context)} to get the default NFC - * adapter for this Android device. - * - * <div class="special reference"> - * <h3>Developer Guides</h3> - * <p>For more information about using NFC, read the - * <a href="{@docRoot}guide/topics/nfc/index.html">Near Field Communication</a> developer guide.</p> - * <p>To perform basic file sharing between devices, read - * <a href="{@docRoot}training/beam-files/index.html">Sharing Files with NFC</a>. - * </div> - */ -public final class NfcAdapter { - static final String TAG = "NFC"; - - private final NfcControllerAlwaysOnListener mControllerAlwaysOnListener; - private final NfcWlcStateListener mNfcWlcStateListener; - private final NfcVendorNciCallbackListener mNfcVendorNciCallbackListener; - - /** - * Intent to start an activity when a tag with NDEF payload is discovered. - * - * <p>The system inspects the first {@link NdefRecord} in the first {@link NdefMessage} and - * looks for a URI, SmartPoster, or MIME record. If a URI or SmartPoster record is found the - * intent will contain the URI in its data field. If a MIME record is found the intent will - * contain the MIME type in its type field. This allows activities to register - * {@link IntentFilter}s targeting specific content on tags. Activities should register the - * most specific intent filters possible to avoid the activity chooser dialog, which can - * disrupt the interaction with the tag as the user interacts with the screen. - * - * <p>If the tag has an NDEF payload this intent is started before - * {@link #ACTION_TECH_DISCOVERED}. If any activities respond to this intent neither - * {@link #ACTION_TECH_DISCOVERED} or {@link #ACTION_TAG_DISCOVERED} will be started. - * - * <p>The MIME type or data URI of this intent are normalized before dispatch - - * so that MIME, URI scheme and URI host are always lower-case. - */ - @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) - public static final String ACTION_NDEF_DISCOVERED = "android.nfc.action.NDEF_DISCOVERED"; - - /** - * Intent to start an activity when a tag is discovered and activities are registered for the - * specific technologies on the tag. - * - * <p>To receive this intent an activity must include an intent filter - * for this action and specify the desired tech types in a - * manifest <code>meta-data</code> entry. Here is an example manfiest entry: - * <pre> - * <activity android:name=".nfc.TechFilter" android:label="NFC/TechFilter"> - * <!-- Add a technology filter --> - * <intent-filter> - * <action android:name="android.nfc.action.TECH_DISCOVERED" /> - * </intent-filter> - * - * <meta-data android:name="android.nfc.action.TECH_DISCOVERED" - * android:resource="@xml/filter_nfc" - * /> - * </activity></pre> - * - * <p>The meta-data XML file should contain one or more <code>tech-list</code> entries - * each consisting or one or more <code>tech</code> entries. The <code>tech</code> entries refer - * to the qualified class name implementing the technology, for example "android.nfc.tech.NfcA". - * - * <p>A tag matches if any of the - * <code>tech-list</code> sets is a subset of {@link Tag#getTechList() Tag.getTechList()}. Each - * of the <code>tech-list</code>s is considered independently and the - * activity is considered a match is any single <code>tech-list</code> matches the tag that was - * discovered. This provides AND and OR semantics for filtering desired techs. Here is an - * example that will match any tag using {@link NfcF} or any tag using {@link NfcA}, - * {@link MifareClassic}, and {@link Ndef}: - * - * <pre> - * <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - * <!-- capture anything using NfcF --> - * <tech-list> - * <tech>android.nfc.tech.NfcF</tech> - * </tech-list> - * - * <!-- OR --> - * - * <!-- capture all MIFARE Classics with NDEF payloads --> - * <tech-list> - * <tech>android.nfc.tech.NfcA</tech> - * <tech>android.nfc.tech.MifareClassic</tech> - * <tech>android.nfc.tech.Ndef</tech> - * </tech-list> - * </resources></pre> - * - * <p>This intent is started after {@link #ACTION_NDEF_DISCOVERED} and before - * {@link #ACTION_TAG_DISCOVERED}. If any activities respond to {@link #ACTION_NDEF_DISCOVERED} - * this intent will not be started. If any activities respond to this intent - * {@link #ACTION_TAG_DISCOVERED} will not be started. - */ - @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) - public static final String ACTION_TECH_DISCOVERED = "android.nfc.action.TECH_DISCOVERED"; - - /** - * Intent to start an activity when a tag is discovered. - * - * <p>This intent will not be started when a tag is discovered if any activities respond to - * {@link #ACTION_NDEF_DISCOVERED} or {@link #ACTION_TECH_DISCOVERED} for the current tag. - */ - @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) - public static final String ACTION_TAG_DISCOVERED = "android.nfc.action.TAG_DISCOVERED"; - - /** - * Broadcast Action: Intent to notify an application that a transaction event has occurred - * on the Secure Element. - * - * <p>This intent will only be sent if the application has requested permission for - * {@link android.Manifest.permission#NFC_TRANSACTION_EVENT} and if the application has the - * necessary access to Secure Element which witnessed the particular event. - */ - @RequiresPermission(android.Manifest.permission.NFC_TRANSACTION_EVENT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_TRANSACTION_DETECTED = - "android.nfc.action.TRANSACTION_DETECTED"; - - /** - * Broadcast Action: Intent to notify if the preferred payment service changed. - * - * <p>This intent will only be sent to the application has requested permission for - * {@link android.Manifest.permission#NFC_PREFERRED_PAYMENT_INFO} and if the application - * has the necessary access to Secure Element which witnessed the particular event. - */ - @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_PREFERRED_PAYMENT_CHANGED = - "android.nfc.action.PREFERRED_PAYMENT_CHANGED"; - - /** - * Broadcast to only the activity that handles ACTION_TAG_DISCOVERED - * @hide - */ - public static final String ACTION_TAG_LEFT_FIELD = "android.nfc.action.TAG_LOST"; - - /** - * Mandatory extra containing the {@link Tag} that was discovered for the - * {@link #ACTION_NDEF_DISCOVERED}, {@link #ACTION_TECH_DISCOVERED}, and - * {@link #ACTION_TAG_DISCOVERED} intents. - */ - public static final String EXTRA_TAG = "android.nfc.extra.TAG"; - - /** - * Extra containing an array of {@link NdefMessage} present on the discovered tag.<p> - * This extra is mandatory for {@link #ACTION_NDEF_DISCOVERED} intents, - * and optional for {@link #ACTION_TECH_DISCOVERED}, and - * {@link #ACTION_TAG_DISCOVERED} intents.<p> - * When this extra is present there will always be at least one - * {@link NdefMessage} element. Most NDEF tags have only one NDEF message, - * but we use an array for future compatibility. - */ - public static final String EXTRA_NDEF_MESSAGES = "android.nfc.extra.NDEF_MESSAGES"; - - /** - * Optional extra containing a byte array containing the ID of the discovered tag for - * the {@link #ACTION_NDEF_DISCOVERED}, {@link #ACTION_TECH_DISCOVERED}, and - * {@link #ACTION_TAG_DISCOVERED} intents. - */ - public static final String EXTRA_ID = "android.nfc.extra.ID"; - - /** - * Broadcast Action: The state of the local NFC adapter has been - * changed. - * <p>For example, NFC has been turned on or off. - * <p>Always contains the extra field {@link #EXTRA_ADAPTER_STATE} - */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_ADAPTER_STATE_CHANGED = - "android.nfc.action.ADAPTER_STATE_CHANGED"; - - /** - * Used as an int extra field in {@link #ACTION_ADAPTER_STATE_CHANGED} - * intents to request the current power state. Possible values are: - * {@link #STATE_OFF}, - * {@link #STATE_TURNING_ON}, - * {@link #STATE_ON}, - * {@link #STATE_TURNING_OFF}, - */ - public static final String EXTRA_ADAPTER_STATE = "android.nfc.extra.ADAPTER_STATE"; - - /** - * Mandatory byte[] extra field in {@link #ACTION_TRANSACTION_DETECTED} - */ - public static final String EXTRA_AID = "android.nfc.extra.AID"; - - /** - * Optional byte[] extra field in {@link #ACTION_TRANSACTION_DETECTED} - */ - public static final String EXTRA_DATA = "android.nfc.extra.DATA"; - - /** - * Mandatory String extra field in {@link #ACTION_TRANSACTION_DETECTED} - * Indicates the Secure Element on which the transaction occurred. - * eSE1...eSEn for Embedded Secure Elements, SIM1...SIMn for UICC/EUICC, etc. - */ - public static final String EXTRA_SECURE_ELEMENT_NAME = "android.nfc.extra.SECURE_ELEMENT_NAME"; - - /** - * Mandatory String extra field in {@link #ACTION_PREFERRED_PAYMENT_CHANGED} - * Indicates the condition when trigger this event. Possible values are: - * {@link #PREFERRED_PAYMENT_LOADED}, - * {@link #PREFERRED_PAYMENT_CHANGED}, - * {@link #PREFERRED_PAYMENT_UPDATED}, - */ - public static final String EXTRA_PREFERRED_PAYMENT_CHANGED_REASON = - "android.nfc.extra.PREFERRED_PAYMENT_CHANGED_REASON"; - /** - * Nfc is enabled and the preferred payment aids are registered. - */ - public static final int PREFERRED_PAYMENT_LOADED = 1; - /** - * User selected another payment application as the preferred payment. - */ - public static final int PREFERRED_PAYMENT_CHANGED = 2; - /** - * Current preferred payment has issued an update (registered/unregistered new aids or has been - * updated itself). - */ - public static final int PREFERRED_PAYMENT_UPDATED = 3; - - public static final int STATE_OFF = 1; - public static final int STATE_TURNING_ON = 2; - public static final int STATE_ON = 3; - public static final int STATE_TURNING_OFF = 4; - - /** - * Possible states from {@link #getAdapterState}. - * - * @hide - */ - @IntDef(prefix = { "STATE_" }, value = { - STATE_OFF, - STATE_TURNING_ON, - STATE_ON, - STATE_TURNING_OFF - }) - @Retention(RetentionPolicy.SOURCE) - public @interface AdapterState{} - - /** - * Flag for use with {@link #enableReaderMode(Activity, ReaderCallback, int, Bundle)}. - * <p> - * Setting this flag enables polling for Nfc-A technology. - */ - public static final int FLAG_READER_NFC_A = 0x1; - - /** - * Flag for use with {@link #enableReaderMode(Activity, ReaderCallback, int, Bundle)}. - * <p> - * Setting this flag enables polling for Nfc-B technology. - */ - public static final int FLAG_READER_NFC_B = 0x2; - - /** - * Flag for use with {@link #enableReaderMode(Activity, ReaderCallback, int, Bundle)}. - * <p> - * Setting this flag enables polling for Nfc-F technology. - */ - public static final int FLAG_READER_NFC_F = 0x4; - - /** - * Flag for use with {@link #enableReaderMode(Activity, ReaderCallback, int, Bundle)}. - * <p> - * Setting this flag enables polling for Nfc-V (ISO15693) technology. - */ - public static final int FLAG_READER_NFC_V = 0x8; - - /** - * Flag for use with {@link #enableReaderMode(Activity, ReaderCallback, int, Bundle)}. - * <p> - * Setting this flag enables polling for NfcBarcode technology. - */ - public static final int FLAG_READER_NFC_BARCODE = 0x10; - - /** @hide */ - @IntDef(flag = true, value = { - FLAG_SET_DEFAULT_TECH, - FLAG_READER_KEEP, - FLAG_READER_DISABLE, - FLAG_READER_NFC_A, - FLAG_READER_NFC_B, - FLAG_READER_NFC_F, - FLAG_READER_NFC_V, - FLAG_READER_NFC_BARCODE - }) - @Retention(RetentionPolicy.SOURCE) - public @interface PollTechnology {} - - /** - * Flag for use with {@link #enableReaderMode(Activity, ReaderCallback, int, Bundle)}. - * <p> - * Setting this flag allows the caller to prevent the - * platform from performing an NDEF check on the tags it - * finds. - */ - public static final int FLAG_READER_SKIP_NDEF_CHECK = 0x80; - - /** - * Flag for use with {@link #enableReaderMode(Activity, ReaderCallback, int, Bundle)}. - * <p> - * Setting this flag allows the caller to prevent the - * platform from playing sounds when it discovers a tag. - */ - public static final int FLAG_READER_NO_PLATFORM_SOUNDS = 0x100; - - /** - * Int Extra for use with {@link #enableReaderMode(Activity, ReaderCallback, int, Bundle)}. - * <p> - * Setting this integer extra allows the calling application to specify - * the delay that the platform will use for performing presence checks - * on any discovered tag. - */ - public static final String EXTRA_READER_PRESENCE_CHECK_DELAY = "presence"; - - /** - * Flag for use with {@link #setDiscoveryTechnology(Activity, int, int)}. - * <p> - * Setting this flag enables listening for Nfc-A technology. - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH) - public static final int FLAG_LISTEN_NFC_PASSIVE_A = 0x1; - - /** - * Flag for use with {@link #setDiscoveryTechnology(Activity, int, int)}. - * <p> - * Setting this flag enables listening for Nfc-B technology. - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH) - public static final int FLAG_LISTEN_NFC_PASSIVE_B = 1 << 1; - - /** - * Flag for use with {@link #setDiscoveryTechnology(Activity, int, int)}. - * <p> - * Setting this flag enables listening for Nfc-F technology. - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH) - public static final int FLAG_LISTEN_NFC_PASSIVE_F = 1 << 2; - - /** - * Flags for use with {@link #setDiscoveryTechnology(Activity, int, int)}. - * <p> - * Setting this flag disables listening. - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH) - public static final int FLAG_LISTEN_DISABLE = 0x0; - - /** - * Flags for use with {@link #setDiscoveryTechnology(Activity, int, int)}. - * <p> - * Setting this flag disables polling. - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH) - public static final int FLAG_READER_DISABLE = 0x0; - - /** - * Flags for use with {@link #setDiscoveryTechnology(Activity, int, int)}. - * <p> - * Setting this flag makes listening to keep the current technology configuration. - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH) - public static final int FLAG_LISTEN_KEEP = 0x80000000; - - /** - * Flags for use with {@link #setDiscoveryTechnology(Activity, int, int)}. - * <p> - * Setting this flag makes polling to keep the current technology configuration. - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH) - public static final int FLAG_READER_KEEP = 0x80000000; - - /** @hide */ - public static final int FLAG_USE_ALL_TECH = 0xff; - - /** @hide */ - @IntDef(flag = true, value = { - FLAG_SET_DEFAULT_TECH, - FLAG_LISTEN_KEEP, - FLAG_LISTEN_DISABLE, - FLAG_LISTEN_NFC_PASSIVE_A, - FLAG_LISTEN_NFC_PASSIVE_B, - FLAG_LISTEN_NFC_PASSIVE_F - }) - @Retention(RetentionPolicy.SOURCE) - public @interface ListenTechnology {} - - /** - * Flag used in {@link #setDiscoveryTechnology(Activity, int, int)}. - * <p> - * Setting this flag changes the default listen or poll tech. - * Only available to privileged apps. - * @hide - */ - @SystemApi - @FlaggedApi(Flags.FLAG_NFC_SET_DEFAULT_DISC_TECH) - @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS) - public static final int FLAG_SET_DEFAULT_TECH = 0x40000000; - - /** - * @hide - * @removed - */ - @SystemApi - @UnsupportedAppUsage - public static final int FLAG_NDEF_PUSH_NO_CONFIRM = 0x1; - - /** @hide */ - public static final String ACTION_HANDOVER_TRANSFER_STARTED = - "android.nfc.action.HANDOVER_TRANSFER_STARTED"; - - /** @hide */ - public static final String ACTION_HANDOVER_TRANSFER_DONE = - "android.nfc.action.HANDOVER_TRANSFER_DONE"; - - /** @hide */ - public static final String EXTRA_HANDOVER_TRANSFER_STATUS = - "android.nfc.extra.HANDOVER_TRANSFER_STATUS"; - - /** @hide */ - public static final int HANDOVER_TRANSFER_STATUS_SUCCESS = 0; - /** @hide */ - public static final int HANDOVER_TRANSFER_STATUS_FAILURE = 1; - - /** @hide */ - public static final String EXTRA_HANDOVER_TRANSFER_URI = - "android.nfc.extra.HANDOVER_TRANSFER_URI"; - - /** - * Broadcast Action: Notify possible NFC transaction blocked because device is locked. - * <p>An external NFC field detected when device locked and SecureNfc enabled. - * @hide - */ - @SystemApi - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - public static final String ACTION_REQUIRE_UNLOCK_FOR_NFC = - "android.nfc.action.REQUIRE_UNLOCK_FOR_NFC"; - - /** - * Intent action to start a NFC resolver activity in a customized share session with list of - * {@link ResolveInfo}. - * @hide - */ - @SystemApi - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - @RequiresPermission(Manifest.permission.SHOW_CUSTOMIZED_RESOLVER) - public static final String ACTION_SHOW_NFC_RESOLVER = "android.nfc.action.SHOW_NFC_RESOLVER"; - - /** - * "Extras" key for an ArrayList of {@link ResolveInfo} records which are to be shown as the - * targets in the customized share session. - * @hide - */ - @SystemApi - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - public static final String EXTRA_RESOLVE_INFOS = "android.nfc.extra.RESOLVE_INFOS"; - - /** - * The requested app is correctly added to the Tag intent app preference. - * - * @see #setTagIntentAppPreferenceForUser(int userId, String pkg, boolean allow) - * @hide - */ - @SystemApi - public static final int TAG_INTENT_APP_PREF_RESULT_SUCCESS = 0; - - /** - * The requested app is not installed on the device. - * - * @see #setTagIntentAppPreferenceForUser(int userId, String pkg, boolean allow) - * @hide - */ - @SystemApi - public static final int TAG_INTENT_APP_PREF_RESULT_PACKAGE_NOT_FOUND = -1; - - /** - * The NfcService is not available. - * - * @see #setTagIntentAppPreferenceForUser(int userId, String pkg, boolean allow) - * @hide - */ - @SystemApi - public static final int TAG_INTENT_APP_PREF_RESULT_UNAVAILABLE = -2; - - /** - * Possible response codes from {@link #setTagIntentAppPreferenceForUser}. - * - * @hide - */ - @IntDef(prefix = { "TAG_INTENT_APP_PREF_RESULT" }, value = { - TAG_INTENT_APP_PREF_RESULT_SUCCESS, - TAG_INTENT_APP_PREF_RESULT_PACKAGE_NOT_FOUND, - TAG_INTENT_APP_PREF_RESULT_UNAVAILABLE}) - @Retention(RetentionPolicy.SOURCE) - public @interface TagIntentAppPreferenceResult {} - - /** - * Mode Type for {@link NfcOemExtension#setControllerAlwaysOnMode(int)}. - * @hide - */ - public static final int CONTROLLER_ALWAYS_ON_MODE_DEFAULT = 1; - - /** - * Mode Type for {@link NfcOemExtension#setControllerAlwaysOnMode(int)}. - * @hide - */ - public static final int CONTROLLER_ALWAYS_ON_DISABLE = 0; - - // Guarded by sLock - static boolean sIsInitialized = false; - static boolean sHasNfcFeature; - static boolean sHasCeFeature; - static boolean sHasNfcWlcFeature; - - static Object sLock = new Object(); - - // Final after first constructor, except for - // attemptDeadServiceRecovery() when NFC crashes - we accept a best effort - // recovery - @UnsupportedAppUsage - static INfcAdapter sService; - static NfcServiceManager.ServiceRegisterer sServiceRegisterer; - static INfcTag sTagService; - static INfcCardEmulation sCardEmulationService; - static INfcFCardEmulation sNfcFCardEmulationService; - static IT4tNdefNfcee sNdefNfceeService; - - /** - * The NfcAdapter object for each application context. - * There is a 1-1 relationship between application context and - * NfcAdapter object. - */ - static HashMap<Context, NfcAdapter> sNfcAdapters = new HashMap(); //guard by NfcAdapter.class - - /** - * NfcAdapter used with a null context. This ctor was deprecated but we have - * to support it for backwards compatibility. New methods that require context - * might throw when called on the null-context NfcAdapter. - */ - static NfcAdapter sNullContextNfcAdapter; // protected by NfcAdapter.class - - final NfcActivityManager mNfcActivityManager; - final Context mContext; - final HashMap<NfcUnlockHandler, INfcUnlockHandler> mNfcUnlockHandlers; - final Object mLock; - final NfcOemExtension mNfcOemExtension; - - ITagRemovedCallback mTagRemovedListener; // protected by mLock - - /** - * A callback to be invoked when the system finds a tag while the foreground activity is - * operating in reader mode. - * <p>Register your {@code ReaderCallback} implementation with {@link - * NfcAdapter#enableReaderMode} and disable it with {@link - * NfcAdapter#disableReaderMode}. - * @see NfcAdapter#enableReaderMode - */ - public interface ReaderCallback { - public void onTagDiscovered(Tag tag); - } - - /** - * A listener to be invoked when NFC controller always on state changes. - * <p>Register your {@code ControllerAlwaysOnListener} implementation with {@link - * NfcAdapter#registerControllerAlwaysOnListener} and disable it with {@link - * NfcAdapter#unregisterControllerAlwaysOnListener}. - * @see #registerControllerAlwaysOnListener - * @hide - */ - @SystemApi - public interface ControllerAlwaysOnListener { - /** - * Called on NFC controller always on state changes - */ - void onControllerAlwaysOnChanged(boolean isEnabled); - } - - /** - * A callback to be invoked when the system successfully delivers your {@link NdefMessage} - * to another device. - * @deprecated this feature is removed. File sharing can work using other technology like - * Bluetooth. - */ - @java.lang.Deprecated - public interface OnNdefPushCompleteCallback { - /** - * Called on successful NDEF push. - * - * <p>This callback is usually made on a binder thread (not the UI thread). - * - * @param event {@link NfcEvent} with the {@link NfcEvent#nfcAdapter} field set - */ - public void onNdefPushComplete(NfcEvent event); - } - - /** - * A callback to be invoked when another NFC device capable of NDEF push (Android Beam) - * is within range. - * <p>Implement this interface and pass it to {@code - * NfcAdapter#setNdefPushMessageCallback setNdefPushMessageCallback()} in order to create an - * {@link NdefMessage} at the moment that another device is within range for NFC. Using this - * callback allows you to create a message with data that might vary based on the - * content currently visible to the user. Alternatively, you can call {@code - * #setNdefPushMessage setNdefPushMessage()} if the {@link NdefMessage} always contains the - * same data. - * @deprecated this feature is removed. File sharing can work using other technology like - * Bluetooth. - */ - @java.lang.Deprecated - public interface CreateNdefMessageCallback { - /** - * Called to provide a {@link NdefMessage} to push. - * - * <p>This callback is usually made on a binder thread (not the UI thread). - * - * <p>Called when this device is in range of another device - * that might support NDEF push. It allows the application to - * create the NDEF message only when it is required. - * - * <p>NDEF push cannot occur until this method returns, so do not - * block for too long. - * - * <p>The Android operating system will usually show a system UI - * on top of your activity during this time, so do not try to request - * input from the user to complete the callback, or provide custom NDEF - * push UI. The user probably will not see it. - * - * @param event {@link NfcEvent} with the {@link NfcEvent#nfcAdapter} field set - * @return NDEF message to push, or null to not provide a message - */ - public NdefMessage createNdefMessage(NfcEvent event); - } - - - /** - * @deprecated this feature is removed. File sharing can work using other technology like - * Bluetooth. - */ - @java.lang.Deprecated - public interface CreateBeamUrisCallback { - public Uri[] createBeamUris(NfcEvent event); - } - - /** - * A callback that is invoked when a tag is removed from the field. - * @see NfcAdapter#ignore - */ - public interface OnTagRemovedListener { - void onTagRemoved(); - } - - /** - * A callback to be invoked when an application has registered as a - * handler to unlock the device given an NFC tag at the lockscreen. - * @hide - */ - @SystemApi - public interface NfcUnlockHandler { - /** - * Called at the lock screen to attempt to unlock the device with the given tag. - * @param tag the detected tag, to be used to unlock the device - * @return true if the device was successfully unlocked - */ - public boolean onUnlockAttempted(Tag tag); - } - - /** - * Return list of Secure Elements which support off host card emulation. - * - * @return List<String> containing secure elements on the device which supports - * off host card emulation. eSE for Embedded secure element, - * SIM for UICC/EUICC and so on. - * @hide - */ - public @NonNull List<String> getSupportedOffHostSecureElements() { - if (mContext == null) { - throw new UnsupportedOperationException("You need a context on NfcAdapter to use the " - + " getSupportedOffHostSecureElements APIs"); - } - List<String> offHostSE = new ArrayList<String>(); - PackageManager pm = mContext.getPackageManager(); - if (pm == null) { - Log.e(TAG, "Cannot get package manager, assuming no off-host CE feature"); - return offHostSE; - } - if (pm.hasSystemFeature(PackageManager.FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC)) { - offHostSE.add("SIM"); - } - if (pm.hasSystemFeature(PackageManager.FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE)) { - offHostSE.add("eSE"); - } - return offHostSE; - } - - private static void retrieveServiceRegisterer() { - if (sServiceRegisterer == null) { - NfcServiceManager manager = NfcFrameworkInitializer.getNfcServiceManager(); - if (manager == null) { - Log.e(TAG, "NfcServiceManager is null"); - throw new UnsupportedOperationException(); - } - sServiceRegisterer = manager.getNfcManagerServiceRegisterer(); - } - } - - /** - * Returns the NfcAdapter for application context, - * or throws if NFC is not available. - * @hide - */ - @UnsupportedAppUsage - public static synchronized NfcAdapter getNfcAdapter(Context context) { - if (context == null) { - if (sNullContextNfcAdapter == null) { - sNullContextNfcAdapter = new NfcAdapter(null); - } - return sNullContextNfcAdapter; - } - if (!sIsInitialized) { - PackageManager pm; - pm = context.getPackageManager(); - sHasNfcFeature = pm.hasSystemFeature(PackageManager.FEATURE_NFC); - sHasCeFeature = - pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION) - || pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION_NFCF) - || pm.hasSystemFeature(PackageManager.FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC) - || pm.hasSystemFeature(PackageManager.FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE); - sHasNfcWlcFeature = pm.hasSystemFeature(PackageManager.FEATURE_NFC_CHARGING); - /* is this device meant to have NFC */ - if (!sHasNfcFeature && !sHasCeFeature && !sHasNfcWlcFeature) { - Log.v(TAG, "this device does not have NFC support"); - throw new UnsupportedOperationException(); - } - retrieveServiceRegisterer(); - sService = getServiceInterface(); - if (sService == null) { - Log.e(TAG, "could not retrieve NFC service"); - throw new UnsupportedOperationException(); - } - if (sHasNfcFeature) { - try { - sTagService = sService.getNfcTagInterface(); - } catch (RemoteException e) { - sTagService = null; - Log.e(TAG, "could not retrieve NFC Tag service"); - throw new UnsupportedOperationException(); - } - } - if (sHasCeFeature) { - try { - sNfcFCardEmulationService = sService.getNfcFCardEmulationInterface(); - } catch (RemoteException e) { - sNfcFCardEmulationService = null; - Log.e(TAG, "could not retrieve NFC-F card emulation service"); - throw new UnsupportedOperationException(); - } - try { - sCardEmulationService = sService.getNfcCardEmulationInterface(); - } catch (RemoteException e) { - sCardEmulationService = null; - Log.e(TAG, "could not retrieve card emulation service"); - throw new UnsupportedOperationException(); - } - } - try { - sNdefNfceeService = sService.getT4tNdefNfceeInterface(); - } catch (RemoteException e) { - sNdefNfceeService = null; - Log.e(TAG, "could not retrieve NDEF NFCEE service"); - throw new UnsupportedOperationException(); - } - sIsInitialized = true; - } - NfcAdapter adapter = sNfcAdapters.get(context); - if (adapter == null) { - adapter = new NfcAdapter(context); - sNfcAdapters.put(context, adapter); - } - return adapter; - } - - /** get handle to NFC service interface */ - private static INfcAdapter getServiceInterface() { - /* get a handle to NFC service */ - IBinder b = sServiceRegisterer.get(); - if (b == null) { - return null; - } - return INfcAdapter.Stub.asInterface(b); - } - - /** - * Helper to get the default NFC Adapter. - * <p> - * Most Android devices will only have one NFC Adapter (NFC Controller). - * <p> - * This helper is the equivalent of: - * <pre> - * NfcManager manager = (NfcManager) context.getSystemService(Context.NFC_SERVICE); - * NfcAdapter adapter = manager.getDefaultAdapter();</pre> - * @param context the calling application's context - * - * @return the default NFC adapter, or null if no NFC adapter exists - */ - public static NfcAdapter getDefaultAdapter(Context context) { - if (context == null) { - throw new IllegalArgumentException("context cannot be null"); - } - context = context.getApplicationContext(); - if (context == null) { - throw new IllegalArgumentException( - "context not associated with any application (using a mock context?)"); - } - retrieveServiceRegisterer(); - if (sServiceRegisterer.tryGet() == null) { - if (sIsInitialized) { - synchronized (NfcAdapter.class) { - /* Stale sService pointer */ - if (sIsInitialized) sIsInitialized = false; - } - } - return null; - } - /* Try to initialize the service */ - NfcManager manager = (NfcManager) context.getSystemService(Context.NFC_SERVICE); - if (manager == null) { - // NFC not available - return null; - } - return manager.getDefaultAdapter(); - } - - /** - * Legacy NfcAdapter getter, always use {@link #getDefaultAdapter(Context)} instead.<p> - * This method was deprecated at API level 10 (Gingerbread MR1) because a context is required - * for many NFC API methods. Those methods will fail when called on an NfcAdapter - * object created from this method.<p> - * @deprecated use {@link #getDefaultAdapter(Context)} - * @hide - */ - @Deprecated - @UnsupportedAppUsage - public static NfcAdapter getDefaultAdapter() { - // introduced in API version 9 (GB 2.3) - // deprecated in API version 10 (GB 2.3.3) - // removed from public API in version 16 (ICS MR2) - // should maintain as a hidden API for binary compatibility for a little longer - Log.w(TAG, "WARNING: NfcAdapter.getDefaultAdapter() is deprecated, use " + - "NfcAdapter.getDefaultAdapter(Context) instead", new Exception()); - - return NfcAdapter.getNfcAdapter(null); - } - - NfcAdapter(Context context) { - mContext = context; - mNfcActivityManager = new NfcActivityManager(this); - mNfcUnlockHandlers = new HashMap<NfcUnlockHandler, INfcUnlockHandler>(); - mTagRemovedListener = null; - mLock = new Object(); - mControllerAlwaysOnListener = new NfcControllerAlwaysOnListener(getService()); - mNfcWlcStateListener = new NfcWlcStateListener(getService()); - mNfcVendorNciCallbackListener = new NfcVendorNciCallbackListener(getService()); - mNfcOemExtension = new NfcOemExtension(mContext, this); - } - - /** - * @hide - */ - @UnsupportedAppUsage - public Context getContext() { - return mContext; - } - - /** - * Returns the binder interface to the service. - * @hide - */ - @UnsupportedAppUsage - public static INfcAdapter getService() { - isEnabledStatic(); // NOP call to recover sService if it is stale - return sService; - } - - /** - * Returns the binder interface to the tag service. - * @hide - */ - public static INfcTag getTagService() { - isEnabledStatic(); // NOP call to recover sTagService if it is stale - return sTagService; - } - - /** - * Returns the binder interface to the card emulation service. - * @hide - */ - public static INfcCardEmulation getCardEmulationService() { - isEnabledStatic(); - return sCardEmulationService; - } - - /** - * Returns the binder interface to the NFC-F card emulation service. - * @hide - */ - public static INfcFCardEmulation getNfcFCardEmulationService() { - isEnabledStatic(); - return sNfcFCardEmulationService; - } - - /** - * Returns the binder interface to the NFC-DTA test interface. - * @hide - */ - public INfcDta getNfcDtaInterface() { - if (mContext == null) { - throw new UnsupportedOperationException("You need a context on NfcAdapter to use the " - + " NFC extras APIs"); - } - return callServiceReturn(() -> sService.getNfcDtaInterface(mContext.getPackageName()), - null); - - } - - /** - * NFC service dead - attempt best effort recovery - * @hide - */ - @UnsupportedAppUsage - public static void attemptDeadServiceRecovery(RemoteException e) { - Log.e(TAG, "NFC service dead - attempting to recover", e); - INfcAdapter service = getServiceInterface(); - if (service == null) { - Log.e(TAG, "could not retrieve NFC service during service recovery"); - // nothing more can be done now, sService is still stale, we'll hit - // this recovery path again later - e.rethrowAsRuntimeException(); - } - // assigning to sService is not thread-safe, but this is best-effort code - // and on a well-behaved system should never happen - sService = service; - if (sHasNfcFeature) { - try { - sTagService = service.getNfcTagInterface(); - } catch (RemoteException ee) { - sTagService = null; - Log.e(TAG, "could not retrieve NFC tag service during service recovery"); - // nothing more can be done now, sService is still stale, we'll hit - // this recovery path again later - ee.rethrowAsRuntimeException(); - } - } - - if (sHasCeFeature) { - try { - sCardEmulationService = service.getNfcCardEmulationInterface(); - } catch (RemoteException ee) { - sCardEmulationService = null; - Log.e(TAG, - "could not retrieve NFC card emulation service during service recovery"); - } - - try { - sNfcFCardEmulationService = service.getNfcFCardEmulationInterface(); - } catch (RemoteException ee) { - sNfcFCardEmulationService = null; - Log.e(TAG, - "could not retrieve NFC-F card emulation service during service recovery"); - } - } - } - - private static boolean isCardEmulationEnabled() { - if (sHasCeFeature) { - return (sCardEmulationService != null || sNfcFCardEmulationService != null); - } - return false; - } - - private static boolean isTagReadingEnabled() { - if (sHasNfcFeature) { - return sTagService != null; - } - return false; - } - - private static boolean isEnabledStatic() { - boolean serviceState = callServiceReturn(() -> sService.getState() == STATE_ON, false); - return serviceState - && (isTagReadingEnabled() || isCardEmulationEnabled() || sHasNfcWlcFeature); - } - - /** - * Return true if this NFC Adapter has any features enabled. - * - * <p>If this method returns false, the NFC hardware is guaranteed not to - * generate or respond to any NFC communication over its NFC radio. - * <p>Applications can use this to check if NFC is enabled. Applications - * can request Settings UI allowing the user to toggle NFC using: - * <p><pre>startActivity(new Intent(Settings.ACTION_NFC_SETTINGS))</pre> - * - * @see android.provider.Settings#ACTION_NFC_SETTINGS - * @return true if this NFC Adapter has any features enabled - */ - public boolean isEnabled() { - return isEnabledStatic(); - } - - /** - * Return the state of this NFC Adapter. - * - * <p>Returns one of {@link #STATE_ON}, {@link #STATE_TURNING_ON}, - * {@link #STATE_OFF}, {@link #STATE_TURNING_OFF}. - * - * <p>{@link #isEnabled()} is equivalent to - * <code>{@link #getAdapterState()} == {@link #STATE_ON}</code> - * - * @return the current state of this NFC adapter - * - * @hide - */ - @SystemApi - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - public @AdapterState int getAdapterState() { - return callServiceReturn(() -> sService.getState(), NfcAdapter.STATE_OFF); - - } - - /** - * Enable NFC hardware. - * - * <p>This call is asynchronous. Listen for - * {@link #ACTION_ADAPTER_STATE_CHANGED} broadcasts to find out when the - * operation is complete. - * - * <p>This API is only allowed to be called by system apps - * or apps which are Device Owner or Profile Owner. - * - * <p>If this returns true, then either NFC is already on, or - * a {@link #ACTION_ADAPTER_STATE_CHANGED} broadcast will be sent - * to indicate a state transition. If this returns false, then - * there is some problem that prevents an attempt to turn - * NFC on (for example we are in airplane mode and NFC is not - * toggleable in airplane mode on this platform). - * - */ - @FlaggedApi(Flags.FLAG_NFC_STATE_CHANGE) - @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - public boolean enable() { - return callServiceReturn(() -> sService.enable(mContext.getPackageName()), false); - - } - - /** - * Disable NFC hardware. - * - * <p>No NFC features will work after this call, and the hardware - * will not perform or respond to any NFC communication. - * - * <p>This call is asynchronous. Listen for - * {@link #ACTION_ADAPTER_STATE_CHANGED} broadcasts to find out when the - * operation is complete. - * - * <p>This API is only allowed to be called by system apps - * or apps which are Device Owner or Profile Owner. - * - * <p>If this returns true, then either NFC is already off, or - * a {@link #ACTION_ADAPTER_STATE_CHANGED} broadcast will be sent - * to indicate a state transition. If this returns false, then - * there is some problem that prevents an attempt to turn - * NFC off. - * - */ - @FlaggedApi(Flags.FLAG_NFC_STATE_CHANGE) - @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - public boolean disable() { - return callServiceReturn(() -> sService.disable(true, mContext.getPackageName()), - false); - - } - - /** - * Disable NFC hardware. - * @hide - */ - @SystemApi - @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - public boolean disable(boolean persist) { - return callServiceReturn(() -> sService.disable(persist, mContext.getPackageName()), - false); - - } - - /** - * Pauses NFC tag reader mode polling for a {@code timeoutInMs} millisecond. - * In case of {@code timeoutInMs} is zero or invalid polling will be stopped indefinitely - * use {@link #resumePolling() to resume the polling. - * @hide - */ - public void pausePolling(int timeoutInMs) { - callService(() -> sService.pausePolling(timeoutInMs)); - } - - - /** - * Returns whether the device supports observe mode or not. When observe mode is enabled, the - * NFC hardware will listen to NFC readers, but not respond to them. While enabled, observed - * polling frames will be sent to the APDU service (see {@link #setObserveModeEnabled(boolean)}. - * When observe mode is disabled (or if it's not supported), the NFC hardware will automatically - * respond to the reader and proceed with the transaction. - * @return true if the mode is supported, false otherwise. - */ - @FlaggedApi(Flags.FLAG_NFC_OBSERVE_MODE) - public boolean isObserveModeSupported() { - return callServiceReturn(() -> sService.isObserveModeSupported(), false); - } - - /** - * Returns whether Observe Mode is currently enabled or not. - * - * @return true if observe mode is enabled, false otherwise. - */ - - @FlaggedApi(Flags.FLAG_NFC_OBSERVE_MODE) - public boolean isObserveModeEnabled() { - return callServiceReturn(() -> sService.isObserveModeEnabled(), false); - } - - /** - * Controls whether the NFC adapter will allow transactions to proceed or be in observe mode - * and simply observe and notify the APDU service of polling loop frames. See - * {@link #isObserveModeSupported()} for a description of observe mode. Only the package of the - * currently preferred service (the service set as preferred by the current foreground - * application via {@link android.nfc.cardemulation.CardEmulation#setPreferredService(Activity, - * android.content.ComponentName)} or the current Default Wallet Role Holder - * {@link android.app.role.RoleManager#ROLE_WALLET}), otherwise a call to this method will fail - * and return false. - * - * @param enabled false disables observe mode to allow the transaction to proceed while true - * enables observe mode and does not allow transactions to proceed. - * - * @return boolean indicating success or failure. - */ - - @FlaggedApi(Flags.FLAG_NFC_OBSERVE_MODE) - public boolean setObserveModeEnabled(boolean enabled) { - if (mContext == null) { - throw new UnsupportedOperationException("You need a context on NfcAdapter to use the " - + " observe mode APIs"); - } - return callServiceReturn(() -> sService.setObserveMode(enabled, mContext.getPackageName()), - false); - } - - /** - * Resumes default NFC tag reader mode polling for the current device state if polling is - * paused. Calling this while already in polling is a no-op. - * @hide - */ - public void resumePolling() { - callService(() -> sService.resumePolling()); - } - - /** - * Set one or more {@link Uri}s to send using Android Beam (TM). Every - * Uri you provide must have either scheme 'file' or scheme 'content'. - * - * <p>For the data provided through this method, Android Beam tries to - * switch to alternate transports such as Bluetooth to achieve a fast - * transfer speed. Hence this method is very suitable - * for transferring large files such as pictures or songs. - * - * <p>The receiving side will store the content of each Uri in - * a file and present a notification to the user to open the file - * with a {@link android.content.Intent} with action - * {@link android.content.Intent#ACTION_VIEW}. - * If multiple URIs are sent, the {@link android.content.Intent} will refer - * to the first of the stored files. - * - * <p>This method may be called at any time before {@link Activity#onDestroy}, - * but the URI(s) are only made available for Android Beam when the - * specified activity(s) are in resumed (foreground) state. The recommended - * approach is to call this method during your Activity's - * {@link Activity#onCreate} - see sample - * code below. This method does not immediately perform any I/O or blocking work, - * so is safe to call on your main thread. - * - * <p>{@link #setBeamPushUris} and {@link #setBeamPushUrisCallback} - * have priority over both {@link #setNdefPushMessage} and - * {@link #setNdefPushMessageCallback}. - * - * <p>If {@link #setBeamPushUris} is called with a null Uri array, - * and/or {@link #setBeamPushUrisCallback} is called with a null callback, - * then the Uri push will be completely disabled for the specified activity(s). - * - * <p>Code example: - * <pre> - * protected void onCreate(Bundle savedInstanceState) { - * super.onCreate(savedInstanceState); - * NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this); - * if (nfcAdapter == null) return; // NFC not available on this device - * nfcAdapter.setBeamPushUris(new Uri[] {uri1, uri2}, this); - * }</pre> - * And that is it. Only one call per activity is necessary. The Android - * OS will automatically release its references to the Uri(s) and the - * Activity object when it is destroyed if you follow this pattern. - * - * <p>If your Activity wants to dynamically supply Uri(s), - * then set a callback using {@link #setBeamPushUrisCallback} instead - * of using this method. - * - * <p class="note">Do not pass in an Activity that has already been through - * {@link Activity#onDestroy}. This is guaranteed if you call this API - * during {@link Activity#onCreate}. - * - * <p class="note">If this device does not support alternate transports - * such as Bluetooth or WiFI, calling this method does nothing. - * - * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. - * - * @param uris an array of Uri(s) to push over Android Beam - * @param activity activity for which the Uri(s) will be pushed - * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. - * @removed this feature is removed. File sharing can work using other technology like - * Bluetooth. - */ - @java.lang.Deprecated - @UnsupportedAppUsage - public void setBeamPushUris(Uri[] uris, Activity activity) { - synchronized (sLock) { - if (!sHasNfcFeature) { - throw new UnsupportedOperationException(); - } - } - } - - /** - * Set a callback that will dynamically generate one or more {@link Uri}s - * to send using Android Beam (TM). Every Uri the callback provides - * must have either scheme 'file' or scheme 'content'. - * - * <p>For the data provided through this callback, Android Beam tries to - * switch to alternate transports such as Bluetooth to achieve a fast - * transfer speed. Hence this method is very suitable - * for transferring large files such as pictures or songs. - * - * <p>The receiving side will store the content of each Uri in - * a file and present a notification to the user to open the file - * with a {@link android.content.Intent} with action - * {@link android.content.Intent#ACTION_VIEW}. - * If multiple URIs are sent, the {@link android.content.Intent} will refer - * to the first of the stored files. - * - * <p>This method may be called at any time before {@link Activity#onDestroy}, - * but the URI(s) are only made available for Android Beam when the - * specified activity(s) are in resumed (foreground) state. The recommended - * approach is to call this method during your Activity's - * {@link Activity#onCreate} - see sample - * code below. This method does not immediately perform any I/O or blocking work, - * so is safe to call on your main thread. - * - * <p>{@link #setBeamPushUris} and {@link #setBeamPushUrisCallback} - * have priority over both {@link #setNdefPushMessage} and - * {@link #setNdefPushMessageCallback}. - * - * <p>If {@link #setBeamPushUris} is called with a null Uri array, - * and/or {@link #setBeamPushUrisCallback} is called with a null callback, - * then the Uri push will be completely disabled for the specified activity(s). - * - * <p>Code example: - * <pre> - * protected void onCreate(Bundle savedInstanceState) { - * super.onCreate(savedInstanceState); - * NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this); - * if (nfcAdapter == null) return; // NFC not available on this device - * nfcAdapter.setBeamPushUrisCallback(callback, this); - * }</pre> - * And that is it. Only one call per activity is necessary. The Android - * OS will automatically release its references to the Uri(s) and the - * Activity object when it is destroyed if you follow this pattern. - * - * <p class="note">Do not pass in an Activity that has already been through - * {@link Activity#onDestroy}. This is guaranteed if you call this API - * during {@link Activity#onCreate}. - * - * <p class="note">If this device does not support alternate transports - * such as Bluetooth or WiFI, calling this method does nothing. - * - * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. - * - * @param callback callback, or null to disable - * @param activity activity for which the Uri(s) will be pushed - * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. - * @removed this feature is removed. File sharing can work using other technology like - * Bluetooth. - */ - @java.lang.Deprecated - @UnsupportedAppUsage - public void setBeamPushUrisCallback(CreateBeamUrisCallback callback, Activity activity) { - synchronized (sLock) { - if (!sHasNfcFeature) { - throw new UnsupportedOperationException(); - } - } - } - - /** - * Set a static {@link NdefMessage} to send using Android Beam (TM). - * - * <p>This method may be called at any time before {@link Activity#onDestroy}, - * but the NDEF message is only made available for NDEF push when the - * specified activity(s) are in resumed (foreground) state. The recommended - * approach is to call this method during your Activity's - * {@link Activity#onCreate} - see sample - * code below. This method does not immediately perform any I/O or blocking work, - * so is safe to call on your main thread. - * - * <p>Only one NDEF message can be pushed by the currently resumed activity. - * If both {@link #setNdefPushMessage} and - * {@link #setNdefPushMessageCallback} are set, then - * the callback will take priority. - * - * <p>If neither {@link #setNdefPushMessage} or - * {@link #setNdefPushMessageCallback} have been called for your activity, then - * the Android OS may choose to send a default NDEF message on your behalf, - * such as a URI for your application. - * - * <p>If {@link #setNdefPushMessage} is called with a null NDEF message, - * and/or {@link #setNdefPushMessageCallback} is called with a null callback, - * then NDEF push will be completely disabled for the specified activity(s). - * This also disables any default NDEF message the Android OS would have - * otherwise sent on your behalf for those activity(s). - * - * <p>If you want to prevent the Android OS from sending default NDEF - * messages completely (for all activities), you can include a - * {@code <meta-data>} element inside the {@code <application>} - * element of your AndroidManifest.xml file, like this: - * <pre> - * <application ...> - * <meta-data android:name="android.nfc.disable_beam_default" - * android:value="true" /> - * </application></pre> - * - * <p>The API allows for multiple activities to be specified at a time, - * but it is strongly recommended to just register one at a time, - * and to do so during the activity's {@link Activity#onCreate}. For example: - * <pre> - * protected void onCreate(Bundle savedInstanceState) { - * super.onCreate(savedInstanceState); - * NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this); - * if (nfcAdapter == null) return; // NFC not available on this device - * nfcAdapter.setNdefPushMessage(ndefMessage, this); - * }</pre> - * And that is it. Only one call per activity is necessary. The Android - * OS will automatically release its references to the NDEF message and the - * Activity object when it is destroyed if you follow this pattern. - * - * <p>If your Activity wants to dynamically generate an NDEF message, - * then set a callback using {@link #setNdefPushMessageCallback} instead - * of a static message. - * - * <p class="note">Do not pass in an Activity that has already been through - * {@link Activity#onDestroy}. This is guaranteed if you call this API - * during {@link Activity#onCreate}. - * - * <p class="note">For sending large content such as pictures and songs, - * consider using {@link #setBeamPushUris}, which switches to alternate transports - * such as Bluetooth to achieve a fast transfer rate. - * - * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. - * - * @param message NDEF message to push over NFC, or null to disable - * @param activity activity for which the NDEF message will be pushed - * @param activities optional additional activities, however we strongly recommend - * to only register one at a time, and to do so in that activity's - * {@link Activity#onCreate} - * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. - * @removed this feature is removed. File sharing can work using other technology like - * Bluetooth. - */ - @java.lang.Deprecated - @UnsupportedAppUsage - public void setNdefPushMessage(NdefMessage message, Activity activity, - Activity ... activities) { - synchronized (sLock) { - if (!sHasNfcFeature) { - throw new UnsupportedOperationException(); - } - } - } - - /** - * @hide - * @removed - */ - @SystemApi - @UnsupportedAppUsage - public void setNdefPushMessage(NdefMessage message, Activity activity, int flags) { - synchronized (sLock) { - if (!sHasNfcFeature) { - throw new UnsupportedOperationException(); - } - } - } - - /** - * Set a callback that dynamically generates NDEF messages to send using Android Beam (TM). - * - * <p>This method may be called at any time before {@link Activity#onDestroy}, - * but the NDEF message callback can only occur when the - * specified activity(s) are in resumed (foreground) state. The recommended - * approach is to call this method during your Activity's - * {@link Activity#onCreate} - see sample - * code below. This method does not immediately perform any I/O or blocking work, - * so is safe to call on your main thread. - * - * <p>Only one NDEF message can be pushed by the currently resumed activity. - * If both {@link #setNdefPushMessage} and - * {@link #setNdefPushMessageCallback} are set, then - * the callback will take priority. - * - * <p>If neither {@link #setNdefPushMessage} or - * {@link #setNdefPushMessageCallback} have been called for your activity, then - * the Android OS may choose to send a default NDEF message on your behalf, - * such as a URI for your application. - * - * <p>If {@link #setNdefPushMessage} is called with a null NDEF message, - * and/or {@link #setNdefPushMessageCallback} is called with a null callback, - * then NDEF push will be completely disabled for the specified activity(s). - * This also disables any default NDEF message the Android OS would have - * otherwise sent on your behalf for those activity(s). - * - * <p>If you want to prevent the Android OS from sending default NDEF - * messages completely (for all activities), you can include a - * {@code <meta-data>} element inside the {@code <application>} - * element of your AndroidManifest.xml file, like this: - * <pre> - * <application ...> - * <meta-data android:name="android.nfc.disable_beam_default" - * android:value="true" /> - * </application></pre> - * - * <p>The API allows for multiple activities to be specified at a time, - * but it is strongly recommended to just register one at a time, - * and to do so during the activity's {@link Activity#onCreate}. For example: - * <pre> - * protected void onCreate(Bundle savedInstanceState) { - * super.onCreate(savedInstanceState); - * NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this); - * if (nfcAdapter == null) return; // NFC not available on this device - * nfcAdapter.setNdefPushMessageCallback(callback, this); - * }</pre> - * And that is it. Only one call per activity is necessary. The Android - * OS will automatically release its references to the callback and the - * Activity object when it is destroyed if you follow this pattern. - * - * <p class="note">Do not pass in an Activity that has already been through - * {@link Activity#onDestroy}. This is guaranteed if you call this API - * during {@link Activity#onCreate}. - * <p class="note">For sending large content such as pictures and songs, - * consider using {@link #setBeamPushUris}, which switches to alternate transports - * such as Bluetooth to achieve a fast transfer rate. - * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. - * - * @param callback callback, or null to disable - * @param activity activity for which the NDEF message will be pushed - * @param activities optional additional activities, however we strongly recommend - * to only register one at a time, and to do so in that activity's - * {@link Activity#onCreate} - * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. - * @removed this feature is removed. File sharing can work using other technology like - * Bluetooth. - */ - @java.lang.Deprecated - @UnsupportedAppUsage - public void setNdefPushMessageCallback(CreateNdefMessageCallback callback, Activity activity, - Activity ... activities) { - synchronized (sLock) { - if (!sHasNfcFeature) { - throw new UnsupportedOperationException(); - } - } - } - - /** - * Set a callback on successful Android Beam (TM). - * - * <p>This method may be called at any time before {@link Activity#onDestroy}, - * but the callback can only occur when the - * specified activity(s) are in resumed (foreground) state. The recommended - * approach is to call this method during your Activity's - * {@link Activity#onCreate} - see sample - * code below. This method does not immediately perform any I/O or blocking work, - * so is safe to call on your main thread. - * - * <p>The API allows for multiple activities to be specified at a time, - * but it is strongly recommended to just register one at a time, - * and to do so during the activity's {@link Activity#onCreate}. For example: - * <pre> - * protected void onCreate(Bundle savedInstanceState) { - * super.onCreate(savedInstanceState); - * NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this); - * if (nfcAdapter == null) return; // NFC not available on this device - * nfcAdapter.setOnNdefPushCompleteCallback(callback, this); - * }</pre> - * And that is it. Only one call per activity is necessary. The Android - * OS will automatically release its references to the callback and the - * Activity object when it is destroyed if you follow this pattern. - * - * <p class="note">Do not pass in an Activity that has already been through - * {@link Activity#onDestroy}. This is guaranteed if you call this API - * during {@link Activity#onCreate}. - * - * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. - * - * @param callback callback, or null to disable - * @param activity activity for which the NDEF message will be pushed - * @param activities optional additional activities, however we strongly recommend - * to only register one at a time, and to do so in that activity's - * {@link Activity#onCreate} - * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. - * @removed this feature is removed. File sharing can work using other technology like - * Bluetooth. - */ - @java.lang.Deprecated - @UnsupportedAppUsage - public void setOnNdefPushCompleteCallback(OnNdefPushCompleteCallback callback, - Activity activity, Activity ... activities) { - synchronized (sLock) { - if (!sHasNfcFeature) { - throw new UnsupportedOperationException(); - } - } - } - - /** - * Enable foreground dispatch to the given Activity. - * - * <p>This will give priority to the foreground activity when - * dispatching a discovered {@link Tag} to an application. - * - * <p>If any IntentFilters are provided to this method they are used to match dispatch Intents - * for both the {@link NfcAdapter#ACTION_NDEF_DISCOVERED} and - * {@link NfcAdapter#ACTION_TAG_DISCOVERED}. Since {@link NfcAdapter#ACTION_TECH_DISCOVERED} - * relies on meta data outside of the IntentFilter matching for that dispatch Intent is handled - * by passing in the tech lists separately. Each first level entry in the tech list represents - * an array of technologies that must all be present to match. If any of the first level sets - * match then the dispatch is routed through the given PendingIntent. In other words, the second - * level is ANDed together and the first level entries are ORed together. - * - * <p>If you pass {@code null} for both the {@code filters} and {@code techLists} parameters - * that acts a wild card and will cause the foreground activity to receive all tags via the - * {@link NfcAdapter#ACTION_TAG_DISCOVERED} intent. - * - * <p>This method must be called from the main thread, and only when the activity is in the - * foreground (resumed). Also, activities must call {@link #disableForegroundDispatch} before - * the completion of their {@link Activity#onPause} callback to disable foreground dispatch - * after it has been enabled. - * - * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. - * - * @param activity the Activity to dispatch to - * @param intent the PendingIntent to start for the dispatch - * @param filters the IntentFilters to override dispatching for, or null to always dispatch - * @param techLists the tech lists used to perform matching for dispatching of the - * {@link NfcAdapter#ACTION_TECH_DISCOVERED} intent - * @throws IllegalStateException if the Activity is not currently in the foreground - * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. - */ - public void enableForegroundDispatch(Activity activity, PendingIntent intent, - IntentFilter[] filters, String[][] techLists) { - synchronized (sLock) { - if (!sHasNfcFeature) { - throw new UnsupportedOperationException(); - } - } - if (activity == null || intent == null) { - throw new NullPointerException(); - } - final TechListParcel parcel = (techLists != null && techLists.length > 0) - ? new TechListParcel(techLists) - : null; - callService(() -> sService.setForegroundDispatch(intent, filters, parcel)); - } - - /** - * Disable foreground dispatch to the given activity. - * - * <p>After calling {@link #enableForegroundDispatch}, an activity - * must call this method before its {@link Activity#onPause} callback - * completes. - * - * <p>This method must be called from the main thread. - * - * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. - * - * @param activity the Activity to disable dispatch to - * @throws IllegalStateException if the Activity has already been paused - * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. - */ - public void disableForegroundDispatch(Activity activity) { - synchronized (sLock) { - if (!sHasNfcFeature) { - throw new UnsupportedOperationException(); - } - } - callService(() -> sService.setForegroundDispatch(null, null, null)); - } - - /** - * Limit the NFC controller to reader mode while this Activity is in the foreground. - * - * <p>In this mode the NFC controller will only act as an NFC tag reader/writer, - * thus disabling any peer-to-peer (Android Beam) and card-emulation modes of - * the NFC adapter on this device. - * - * <p>Use {@link #FLAG_READER_SKIP_NDEF_CHECK} to prevent the platform from - * performing any NDEF checks in reader mode. Note that this will prevent the - * {@link Ndef} tag technology from being enumerated on the tag, and that - * NDEF-based tag dispatch will not be functional. - * - * <p>For interacting with tags that are emulated on another Android device - * using Android's host-based card-emulation, the recommended flags are - * {@link #FLAG_READER_NFC_A} and {@link #FLAG_READER_SKIP_NDEF_CHECK}. - * - * @param activity the Activity that requests the adapter to be in reader mode - * @param callback the callback to be called when a tag is discovered - * @param flags Flags indicating poll technologies and other optional parameters - * @param extras Additional extras for configuring reader mode. - * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. - */ - public void enableReaderMode(Activity activity, ReaderCallback callback, int flags, - Bundle extras) { - synchronized (sLock) { - if (!sHasNfcFeature) { - throw new UnsupportedOperationException(); - } - } - mNfcActivityManager.enableReaderMode(activity, callback, flags, extras); - } - - /** - * Restore the NFC adapter to normal mode of operation: supporting - * peer-to-peer (Android Beam), card emulation, and polling for - * all supported tag technologies. - * - * @param activity the Activity that currently has reader mode enabled - * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. - */ - public void disableReaderMode(Activity activity) { - synchronized (sLock) { - if (!sHasNfcFeature) { - throw new UnsupportedOperationException(); - } - } - mNfcActivityManager.disableReaderMode(activity); - } - - // Flags arguments to NFC adapter to enable/disable NFC - private static final int DISABLE_POLLING_FLAGS = 0x1000; - private static final int ENABLE_POLLING_FLAGS = 0x0000; - - /** - * Privileged API to enable or disable reader polling. - * Unlike {@link #enableReaderMode(Activity, ReaderCallback, int, Bundle)}, this API does not - * need a foreground activity to control reader mode parameters - * Note: Use with caution! The app is responsible for ensuring that the polling state is - * returned to normal. - * - * @see #enableReaderMode(Activity, ReaderCallback, int, Bundle) for more detailed - * documentation. - * - * @param enablePolling whether to enable or disable polling. - * @hide - */ - @SystemApi - @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - @SuppressLint("VisiblySynchronized") - public void setReaderModePollingEnabled(boolean enable) { - synchronized (sLock) { - if (!sHasNfcFeature) { - throw new UnsupportedOperationException(); - } - } - Binder token = new Binder(); - int flags = enable ? ENABLE_POLLING_FLAGS : DISABLE_POLLING_FLAGS; - callService(() -> sService.setReaderMode( - token, null, flags, null, mContext.getPackageName())); - } - - /** - * Set the NFC controller to enable specific poll/listen technologies, - * as specified in parameters, while this Activity is in the foreground. - * - * Use {@link #FLAG_READER_KEEP} to keep current polling technology. - * Use {@link #FLAG_LISTEN_KEEP} to keep current listenig technology. - * (if the _KEEP flag is specified the other technology flags shouldn't be set - * and are quietly ignored otherwise). - * Use {@link #FLAG_READER_DISABLE} to disable polling. - * Use {@link #FLAG_LISTEN_DISABLE} to disable listening. - * Also refer to {@link #resetDiscoveryTechnology(Activity)} to restore these changes. - * </p> - * The pollTechnology, listenTechnology parameters can be one or several of below list. - * <pre> - * Poll Listen - * Passive A 0x01 (NFC_A) 0x01 (NFC_PASSIVE_A) - * Passive B 0x02 (NFC_B) 0x02 (NFC_PASSIVE_B) - * Passive F 0x04 (NFC_F) 0x04 (NFC_PASSIVE_F) - * ISO 15693 0x08 (NFC_V) - - * Kovio 0x10 (NFC_BARCODE) - - * </pre> - * <p>Example usage in an Activity that requires to disable poll, - * keep current listen technologies: - * <pre> - * protected void onResume() { - * mNfcAdapter = NfcAdapter.getDefaultAdapter(getApplicationContext()); - * mNfcAdapter.setDiscoveryTechnology(this, - * NfcAdapter.FLAG_READER_DISABLE, NfcAdapter.FLAG_LISTEN_KEEP); - * }</pre></p> - * @param activity The Activity that requests NFC controller to enable specific technologies. - * @param pollTechnology Flags indicating poll technologies. - * @param listenTechnology Flags indicating listen technologies. - * @throws UnsupportedOperationException if FEATURE_NFC, - * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF are unavailable. - * - * NOTE: This API overrides all technology flags regardless of the current device state, - * it is incompatible with enableReaderMode() API and the others that either update - * or assume any techlology flag set by the OS. - * Please use with care. - */ - - @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH) - public void setDiscoveryTechnology(@NonNull Activity activity, - @PollTechnology int pollTechnology, @ListenTechnology int listenTechnology) { - - if (listenTechnology == FLAG_LISTEN_DISABLE) { - synchronized (sLock) { - if (!sHasNfcFeature) { - throw new UnsupportedOperationException(); - } - } - } else if (pollTechnology == FLAG_READER_DISABLE) { - synchronized (sLock) { - if (!sHasCeFeature) { - throw new UnsupportedOperationException(); - } - } - } else { - synchronized (sLock) { - if (!sHasNfcFeature || !sHasCeFeature) { - throw new UnsupportedOperationException(); - } - } - } - /* - * Privileged FLAG to set technology mask for all data processed by NFC controller - * Note: Use with caution! The app is responsible for ensuring that the discovery - * technology mask is returned to default. - * Note: FLAG_USE_ALL_TECH used with _KEEP flags will reset the technolody to android default - */ - if (Flags.nfcSetDefaultDiscTech() - && ((pollTechnology & FLAG_SET_DEFAULT_TECH) == FLAG_SET_DEFAULT_TECH - || (listenTechnology & FLAG_SET_DEFAULT_TECH) == FLAG_SET_DEFAULT_TECH)) { - Binder token = new Binder(); - callService( () -> - sService.updateDiscoveryTechnology( - token, pollTechnology, listenTechnology, mContext.getPackageName())); - } else { - mNfcActivityManager.setDiscoveryTech(activity, pollTechnology, listenTechnology); - } - } - - /** - * Restore the poll/listen technologies of NFC controller to its default state, - * which were changed by {@link #setDiscoveryTechnology(Activity , int , int)} - * - * @param activity The Activity that requested to change technologies. - */ - - @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH) - public void resetDiscoveryTechnology(@NonNull Activity activity) { - mNfcActivityManager.resetDiscoveryTech(activity); - } - - /** - * Manually invoke Android Beam to share data. - * - * <p>The Android Beam animation is normally only shown when two NFC-capable - * devices come into range. - * By calling this method, an Activity can invoke the Beam animation directly - * even if no other NFC device is in range yet. The Beam animation will then - * prompt the user to tap another NFC-capable device to complete the data - * transfer. - * - * <p>The main advantage of using this method is that it avoids the need for the - * user to tap the screen to complete the transfer, as this method already - * establishes the direction of the transfer and the consent of the user to - * share data. Callers are responsible for making sure that the user has - * consented to sharing data on NFC tap. - * - * <p>Note that to use this method, the passed in Activity must have already - * set data to share over Beam by using method calls such as - * {@link #setNdefPushMessageCallback} or - * {@link #setBeamPushUrisCallback}. - * - * @param activity the current foreground Activity that has registered data to share - * @return whether the Beam animation was successfully invoked - * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. - * @removed this feature is removed. File sharing can work using other technology like - * Bluetooth. - */ - @java.lang.Deprecated - @UnsupportedAppUsage - public boolean invokeBeam(Activity activity) { - synchronized (sLock) { - if (!sHasNfcFeature) { - throw new UnsupportedOperationException(); - } - } - return false; - } - - /** - * Enable NDEF message push over NFC while this Activity is in the foreground. - * - * <p>You must explicitly call this method every time the activity is - * resumed, and you must call {@link #disableForegroundNdefPush} before - * your activity completes {@link Activity#onPause}. - * - * <p>Strongly recommend to use the new {@link #setNdefPushMessage} - * instead: it automatically hooks into your activity life-cycle, - * so you do not need to call enable/disable in your onResume/onPause. - * - * <p>For NDEF push to function properly the other NFC device must - * support either NFC Forum's SNEP (Simple Ndef Exchange Protocol), or - * Android's "com.android.npp" (Ndef Push Protocol). This was optional - * on Gingerbread level Android NFC devices, but SNEP is mandatory on - * Ice-Cream-Sandwich and beyond. - * - * <p>This method must be called from the main thread. - * - * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. - * - * @param activity foreground activity - * @param message a NDEF Message to push over NFC - * @throws UnsupportedOperationException if FEATURE_NFC is unavailable - * @removed this feature is removed. File sharing can work using other technology like - * Bluetooth. - */ - @Deprecated - @UnsupportedAppUsage - public void enableForegroundNdefPush(Activity activity, NdefMessage message) { - synchronized (sLock) { - if (!sHasNfcFeature) { - throw new UnsupportedOperationException(); - } - } - } - - /** - * Disable NDEF message push over P2P. - * - * <p>After calling {@link #enableForegroundNdefPush}, an activity - * must call this method before its {@link Activity#onPause} callback - * completes. - * - * <p>Strongly recommend to use the new {@link #setNdefPushMessage} - * instead: it automatically hooks into your activity life-cycle, - * so you do not need to call enable/disable in your onResume/onPause. - * - * <p>This method must be called from the main thread. - * - * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. - * - * @param activity the Foreground activity - * @throws UnsupportedOperationException if FEATURE_NFC is unavailable - * @removed this feature is removed. File sharing can work using other technology like - * Bluetooth. - */ - @Deprecated - @UnsupportedAppUsage - public void disableForegroundNdefPush(Activity activity) { - synchronized (sLock) { - if (!sHasNfcFeature) { - throw new UnsupportedOperationException(); - } - } - } - - /** - * Sets Secure NFC feature. - * <p>This API is for the Settings application. - * @return True if successful - * @hide - */ - @SystemApi - @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - public boolean enableSecureNfc(boolean enable) { - if (!sHasNfcFeature && !sHasCeFeature) { - throw new UnsupportedOperationException(); - } - return callServiceReturn(() -> sService.setNfcSecure(enable), false); - - } - - /** - * Checks if the device supports Secure NFC functionality. - * - * @return True if device supports Secure NFC, false otherwise - * @throws UnsupportedOperationException if FEATURE_NFC, - * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF, - * FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC and FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE - * are unavailable - */ - public boolean isSecureNfcSupported() { - if (!sHasNfcFeature && !sHasCeFeature) { - throw new UnsupportedOperationException(); - } - return callServiceReturn(() -> sService.deviceSupportsNfcSecure(), false); - - } - - /** - * Returns information regarding Nfc antennas on the device - * such as their relative positioning on the device. - * - * @return Information on the nfc antenna(s) on the device. - * @throws UnsupportedOperationException if FEATURE_NFC, - * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF, - * FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC and FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE - * are unavailable - */ - @Nullable - public NfcAntennaInfo getNfcAntennaInfo() { - if (!sHasNfcFeature && !sHasCeFeature) { - throw new UnsupportedOperationException(); - } - return callServiceReturn(() -> sService.getNfcAntennaInfo(), null); - - } - - /** - * Checks Secure NFC feature is enabled. - * - * @return True if Secure NFC is enabled, false otherwise - * @throws UnsupportedOperationException if FEATURE_NFC, - * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF, - * FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC and FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE - * are unavailable - * @throws UnsupportedOperationException if device doesn't support - * Secure NFC functionality. {@link #isSecureNfcSupported} - */ - public boolean isSecureNfcEnabled() { - if (!sHasNfcFeature && !sHasCeFeature) { - throw new UnsupportedOperationException(); - } - return callServiceReturn(() -> sService.isNfcSecureEnabled(), false); - - } - - /** - * Sets NFC Reader option feature. - * <p>This API is for the Settings application. - * @return True if successful - * @hide - */ - @SystemApi - @FlaggedApi(Flags.FLAG_ENABLE_NFC_READER_OPTION) - @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - public boolean enableReaderOption(boolean enable) { - if (!sHasNfcFeature) { - throw new UnsupportedOperationException(); - } - return callServiceReturn(() -> - sService.enableReaderOption(enable, mContext.getPackageName()), false); - - } - - /** - * Checks if the device supports NFC Reader option functionality. - * - * @return True if device supports NFC Reader option, false otherwise - * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_READER_OPTION) - public boolean isReaderOptionSupported() { - if (!sHasNfcFeature) { - throw new UnsupportedOperationException(); - } - return callServiceReturn(() -> sService.isReaderOptionSupported(), false); - - } - - /** - * Checks NFC Reader option feature is enabled. - * - * @return True if NFC Reader option is enabled, false otherwise - * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. - * @throws UnsupportedOperationException if device doesn't support - * NFC Reader option functionality. {@link #isReaderOptionSupported} - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_READER_OPTION) - public boolean isReaderOptionEnabled() { - if (!sHasNfcFeature) { - throw new UnsupportedOperationException(); - } - return callServiceReturn(() -> sService.isReaderOptionEnabled(), false); - - } - - /** - * Enable NDEF Push feature. - * <p>This API is for the Settings application. - * @hide - * @removed - */ - @SystemApi - @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - @UnsupportedAppUsage - public boolean enableNdefPush() { - return false; - } - - /** - * Disable NDEF Push feature. - * <p>This API is for the Settings application. - * @hide - * @removed - */ - @SystemApi - @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - @UnsupportedAppUsage - public boolean disableNdefPush() { - return false; - } - - /** - * Return true if the NDEF Push (Android Beam) feature is enabled. - * <p>This function will return true only if both NFC is enabled, and the - * NDEF Push feature is enabled. - * <p>Note that if NFC is enabled but NDEF Push is disabled then this - * device can still <i>receive</i> NDEF messages, it just cannot send them. - * <p>Applications cannot directly toggle the NDEF Push feature, but they - * can request Settings UI allowing the user to toggle NDEF Push using - * <code>startActivity(new Intent(Settings.ACTION_NFCSHARING_SETTINGS))</code> - * <p>Example usage in an Activity that requires NDEF Push: - * <p><pre> - * protected void onResume() { - * super.onResume(); - * if (!nfcAdapter.isEnabled()) { - * startActivity(new Intent(Settings.ACTION_NFC_SETTINGS)); - * } else if (!nfcAdapter.isNdefPushEnabled()) { - * startActivity(new Intent(Settings.ACTION_NFCSHARING_SETTINGS)); - * } - * }</pre> - * - * @see android.provider.Settings#ACTION_NFCSHARING_SETTINGS - * @return true if NDEF Push feature is enabled - * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. - * @removed this feature is removed. File sharing can work using other technology like - * Bluetooth. - */ - @java.lang.Deprecated - @UnsupportedAppUsage - public boolean isNdefPushEnabled() { - synchronized (sLock) { - if (!sHasNfcFeature) { - throw new UnsupportedOperationException(); - } - } - return false; - } - - /** - * Signals that you are no longer interested in communicating with an NFC tag - * for as long as it remains in range. - * - * All future attempted communication to this tag will fail with {@link IOException}. - * The NFC controller will be put in a low-power polling mode, allowing the device - * to save power in cases where it's "attached" to a tag all the time (e.g. a tag in - * car dock). - * - * Additionally the debounceMs parameter allows you to specify for how long the tag needs - * to have gone out of range, before it will be dispatched again. - * - * Note: the NFC controller typically polls at a pretty slow interval (100 - 500 ms). - * This means that if the tag repeatedly goes in and out of range (for example, in - * case of a flaky connection), and the controller happens to poll every time the - * tag is out of range, it *will* re-dispatch the tag after debounceMs, despite the tag - * having been "in range" during the interval. - * - * Note 2: if a tag with another UID is detected after this API is called, its effect - * will be cancelled; if this tag shows up before the amount of time specified in - * debounceMs, it will be dispatched again. - * - * Note 3: some tags have a random UID, in which case this API won't work reliably. - * - * @param tag the {@link android.nfc.Tag Tag} to ignore. - * @param debounceMs minimum amount of time the tag needs to be out of range before being - * dispatched again. - * @param tagRemovedListener listener to be called when the tag is removed from the field. - * Note that this will only be called if the tag has been out of range - * for at least debounceMs, or if another tag came into range before - * debounceMs. May be null in case you don't want a callback. - * @param handler the {@link android.os.Handler Handler} that will be used for delivering - * the callback. if the handler is null, then the thread used for delivering - * the callback is unspecified. - * @return false if the tag couldn't be found (or has already gone out of range), true otherwise - */ - public boolean ignore(final Tag tag, int debounceMs, - final OnTagRemovedListener tagRemovedListener, final Handler handler) { - ITagRemovedCallback.Stub iListener = null; - if (tagRemovedListener != null) { - iListener = new ITagRemovedCallback.Stub() { - @Override - public void onTagRemoved() throws RemoteException { - if (handler != null) { - handler.post(new Runnable() { - @Override - public void run() { - tagRemovedListener.onTagRemoved(); - } - }); - } else { - tagRemovedListener.onTagRemoved(); - } - synchronized (mLock) { - mTagRemovedListener = null; - } - } - }; - } - synchronized (mLock) { - mTagRemovedListener = iListener; - } - final ITagRemovedCallback.Stub passedListener = iListener; - return callServiceReturn(() -> - sService.ignore(tag.getServiceHandle(), debounceMs, passedListener), false); - } - - /** - * Inject a mock NFC tag.<p> - * Used for testing purposes. - * <p class="note">Requires the - * {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} permission. - * @hide - */ - public void dispatch(Tag tag) { - if (tag == null) { - throw new NullPointerException("tag cannot be null"); - } - callService(() -> sService.dispatch(tag)); - } - - /** - * Registers a new NFC unlock handler with the NFC service. - * - * <p />NFC unlock handlers are intended to unlock the keyguard in the presence of a trusted - * NFC device. The handler should return true if it successfully authenticates the user and - * unlocks the keyguard. - * - * <p /> The parameter {@code tagTechnologies} determines which Tag technologies will be polled for - * at the lockscreen. Polling for less tag technologies reduces latency, and so it is - * strongly recommended to only provide the Tag technologies that the handler is expected to - * receive. There must be at least one tag technology provided, otherwise the unlock handler - * is ignored. - * - * @hide - */ - @SystemApi - @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - public boolean addNfcUnlockHandler(final NfcUnlockHandler unlockHandler, - String[] tagTechnologies) { - synchronized (sLock) { - if (!sHasNfcFeature) { - throw new UnsupportedOperationException(); - } - } - // If there are no tag technologies, don't bother adding unlock handler - if (tagTechnologies.length == 0) { - return false; - } - - try { - synchronized (mLock) { - if (mNfcUnlockHandlers.containsKey(unlockHandler)) { - // update the tag technologies - callService(() -> { - sService.removeNfcUnlockHandler(mNfcUnlockHandlers.get(unlockHandler)); - mNfcUnlockHandlers.remove(unlockHandler); - }); - } - - INfcUnlockHandler.Stub iHandler = new INfcUnlockHandler.Stub() { - @Override - public boolean onUnlockAttempted(Tag tag) throws RemoteException { - return unlockHandler.onUnlockAttempted(tag); - } - }; - return callServiceReturn(() -> { - sService.addNfcUnlockHandler( - iHandler, Tag.getTechCodesFromStrings(tagTechnologies)); - mNfcUnlockHandlers.put(unlockHandler, iHandler); - return true; - }, false); - } - } catch (IllegalArgumentException e) { - Log.e(TAG, "Unable to register LockscreenDispatch", e); - return false; - } - - } - - /** - * Removes a previously registered unlock handler. Also removes the tag technologies - * associated with the removed unlock handler. - * - * @hide - */ - @SystemApi - @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - public boolean removeNfcUnlockHandler(NfcUnlockHandler unlockHandler) { - synchronized (sLock) { - if (!sHasNfcFeature) { - throw new UnsupportedOperationException(); - } - } - synchronized (mLock) { - if (mNfcUnlockHandlers.containsKey(unlockHandler)) { - return callServiceReturn(() -> { - sService.removeNfcUnlockHandler(mNfcUnlockHandlers.remove(unlockHandler)); - return true; - }, false); - } - return true; - } - } - - /** - * @hide - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public INfcAdapterExtras getNfcAdapterExtrasInterface() { - if (mContext == null) { - throw new UnsupportedOperationException("You need a context on NfcAdapter to use the " - + " NFC extras APIs"); - } - return callServiceReturn(() -> - sService.getNfcAdapterExtrasInterface(mContext.getPackageName()), null); - - } - - void enforceResumed(Activity activity) { - if (!activity.isResumed()) { - throw new IllegalStateException("API cannot be called while activity is paused"); - } - } - - int getSdkVersion() { - if (mContext == null) { - return android.os.Build.VERSION_CODES.GINGERBREAD; // best guess - } else { - return mContext.getApplicationInfo().targetSdkVersion; - } - } - - /** - * Sets NFC controller always on feature. - * <p>This API is for the NFCC internal state management. It allows to discriminate - * the controller function from the NFC function by keeping the NFC controller on without - * any NFC RF enabled if necessary. - * <p>This call is asynchronous. Register a listener {@link ControllerAlwaysOnListener} - * by {@link #registerControllerAlwaysOnListener} to find out when the operation is - * complete. - * <p>If this returns true, then either NFCC always on state has been set based on the value, - * or a {@link ControllerAlwaysOnListener#onControllerAlwaysOnChanged(boolean)} will be invoked - * to indicate the state change. - * If this returns false, then there is some problem that prevents an attempt to turn NFCC - * always on. - * @param value if true the NFCC will be kept on (with no RF enabled if NFC adapter is - * disabled), if false the NFCC will follow completely the Nfc adapter state. - * @throws UnsupportedOperationException if FEATURE_NFC, - * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF, - * FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC and FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE - * are unavailable - * @return true if feature is supported by the device and operation has been initiated, - * false if the feature is not supported by the device. - * @hide - */ - @SystemApi - @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) - public boolean setControllerAlwaysOn(boolean value) { - if (!sHasNfcFeature && !sHasCeFeature) { - throw new UnsupportedOperationException(); - } - int mode = value ? CONTROLLER_ALWAYS_ON_MODE_DEFAULT : CONTROLLER_ALWAYS_ON_DISABLE; - try { - callService(() -> sService.setControllerAlwaysOn(mode)); - } catch (UnsupportedOperationException e) { - return false; - } - return true; - } - - /** - * Checks NFC controller always on feature is enabled. - * - * @return True if NFC controller always on is enabled, false otherwise - * @throws UnsupportedOperationException if FEATURE_NFC, - * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF, - * FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC and FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE - * are unavailable - * @hide - */ - @SystemApi - @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) - public boolean isControllerAlwaysOn() { - return callServiceReturn(() -> sService.isControllerAlwaysOn(), false); - - } - - /** - * Checks if the device supports NFC controller always on functionality. - * - * @return True if device supports NFC controller always on, false otherwise - * @throws UnsupportedOperationException if FEATURE_NFC, - * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF, - * FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC and FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE - * are unavailable - * @hide - */ - @SystemApi - @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) - public boolean isControllerAlwaysOnSupported() { - if (!sHasNfcFeature && !sHasCeFeature) { - throw new UnsupportedOperationException(); - } - return callServiceReturn(() -> sService.isControllerAlwaysOnSupported(), false); - - } - - /** - * Register a {@link ControllerAlwaysOnListener} to listen for NFC controller always on - * state changes - * <p>The provided listener will be invoked by the given {@link Executor}. - * - * @param executor an {@link Executor} to execute given listener - * @param listener user implementation of the {@link ControllerAlwaysOnListener} - * @hide - */ - @SystemApi - @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) - public void registerControllerAlwaysOnListener( - @NonNull @CallbackExecutor Executor executor, - @NonNull ControllerAlwaysOnListener listener) { - mControllerAlwaysOnListener.register(executor, listener); - } - - /** - * Unregister the specified {@link ControllerAlwaysOnListener} - * <p>The same {@link ControllerAlwaysOnListener} object used when calling - * {@link #registerControllerAlwaysOnListener(Executor, ControllerAlwaysOnListener)} - * must be used. - * - * <p>Listeners are automatically unregistered when application process goes away - * - * @param listener user implementation of the {@link ControllerAlwaysOnListener} - * @hide - */ - @SystemApi - @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) - public void unregisterControllerAlwaysOnListener( - @NonNull ControllerAlwaysOnListener listener) { - mControllerAlwaysOnListener.unregister(listener); - } - - - /** - * Sets whether we dispatch NFC Tag intents to the package. - * - * <p>{@link #ACTION_NDEF_DISCOVERED}, {@link #ACTION_TECH_DISCOVERED} or - * {@link #ACTION_TAG_DISCOVERED} will not be dispatched to an Activity if its package is - * disallowed. - * <p>An app is added to the preference list with the allowed flag set to {@code true} - * when a Tag intent is dispatched to the package for the first time. This API is called - * by settings to note that the user wants to change this default preference. - * - * @param userId the user to whom this package name will belong to - * @param pkg the full name (i.e. com.google.android.tag) of the package that will be added to - * the preference list - * @param allow {@code true} to allow dispatching Tag intents to the package's activity, - * {@code false} otherwise - * @return the {@link #TagIntentAppPreferenceResult} value - * @throws UnsupportedOperationException if {@link isTagIntentAppPreferenceSupported} returns - * {@code false} - * - * @hide - */ - @SystemApi - @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - @TagIntentAppPreferenceResult - public int setTagIntentAppPreferenceForUser(@UserIdInt int userId, - @NonNull String pkg, boolean allow) { - Objects.requireNonNull(pkg, "pkg cannot be null"); - if (!isTagIntentAppPreferenceSupported()) { - Log.e(TAG, "TagIntentAppPreference is not supported"); - throw new UnsupportedOperationException(); - } - return callServiceReturn(() -> - sService.setTagIntentAppPreferenceForUser(userId, pkg, allow), - TAG_INTENT_APP_PREF_RESULT_UNAVAILABLE); - } - - - /** - * Get the Tag dispatch preference list of the UserId. - * - * <p>This returns a mapping of package names for this user id to whether we dispatch Tag - * intents to the package. {@link #ACTION_NDEF_DISCOVERED}, {@link #ACTION_TECH_DISCOVERED} or - * {@link #ACTION_TAG_DISCOVERED} will not be dispatched to an Activity if its package is - * mapped to {@code false}. - * <p>There are three different possible cases: - * <p>A package not being in the preference list. - * It does not contain any Tag intent filters or the user never triggers a Tag detection that - * matches the intent filter of the package. - * <p>A package being mapped to {@code true}. - * When a package has been launched by a tag detection for the first time, the package name is - * put to the map and by default mapped to {@code true}. The package will receive Tag intents as - * usual. - * <p>A package being mapped to {@code false}. - * The user chooses to disable this package and it will not receive any Tag intents anymore. - * - * @param userId the user to whom this preference list will belong to - * @return a map of the UserId which indicates the mapping from package name to - * boolean(allow status), otherwise return an empty map - * @throws UnsupportedOperationException if {@link isTagIntentAppPreferenceSupported} returns - * {@code false} - * - * @hide - */ - @SystemApi - @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - @NonNull - public Map<String, Boolean> getTagIntentAppPreferenceForUser(@UserIdInt int userId) { - if (!isTagIntentAppPreferenceSupported()) { - Log.e(TAG, "TagIntentAppPreference is not supported"); - throw new UnsupportedOperationException(); - } - return callServiceReturn( () -> - sService.getTagIntentAppPreferenceForUser(userId), Collections.emptyMap()); - } - - /** - * Checks if the device supports Tag Intent App Preference functionality. - * - * When supported, {@link #ACTION_NDEF_DISCOVERED}, {@link #ACTION_TECH_DISCOVERED} or - * {@link #ACTION_TAG_DISCOVERED} will not be dispatched to an Activity if - * {@link isTagIntentAllowed} returns {@code false}. - * - * @return {@code true} if the device supports Tag application preference, {@code false} - * otherwise - * @throws UnsupportedOperationException if FEATURE_NFC is unavailable - */ - @FlaggedApi(Flags.FLAG_NFC_CHECK_TAG_INTENT_PREFERENCE) - public boolean isTagIntentAppPreferenceSupported() { - if (!sHasNfcFeature) { - throw new UnsupportedOperationException(); - } - return callServiceReturn(() -> sService.isTagIntentAppPreferenceSupported(), false); - } - - /** - * Notifies the system of a new polling loop. - * - * @param frame is the new frame. - * - * @hide - */ - @TestApi - @FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP) - public void notifyPollingLoop(@NonNull PollingFrame pollingFrame) { - callService(() -> sService.notifyPollingLoop(pollingFrame)); - } - - - /** - * Notifies the system of new HCE data for tests. - * - * @hide - */ - @FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP) - public void notifyTestHceData(int technology, byte[] data) { - callService(() -> sService.notifyTestHceData(technology, data)); - } - - /** @hide */ - interface ServiceCall { - void call() throws RemoteException; - } - /** @hide */ - static void callService(ServiceCall call) { - try { - if (sService == null) { - attemptDeadServiceRecovery(new RemoteException("NFC Service is null")); - } - call.call(); - } catch (RemoteException e) { - attemptDeadServiceRecovery(e); - try { - call.call(); - } catch (RemoteException ee) { - ee.rethrowAsRuntimeException(); - } - } - } - /** @hide */ - interface ServiceCallReturn<T> { - T call() throws RemoteException; - } - /** @hide */ - static <T> T callServiceReturn(ServiceCallReturn<T> call, T defaultReturn) { - try { - if (sService == null) { - attemptDeadServiceRecovery(new RemoteException("NFC Service is null")); - } - return call.call(); - } catch (RemoteException e) { - attemptDeadServiceRecovery(e); - // Try one more time - try { - return call.call(); - } catch (RemoteException ee) { - ee.rethrowAsRuntimeException(); - } - } - return defaultReturn; - } - - /** - * Notifies the system of a an HCE session being deactivated. - * * - * @hide - */ - @TestApi - @FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP) - public void notifyHceDeactivated() { - callService(() -> sService.notifyHceDeactivated()); - } - - /** - * Sets NFC charging feature. - * <p>This API is for the Settings application. - * @return True if successful - * @hide - */ - @SystemApi - @FlaggedApi(Flags.FLAG_ENABLE_NFC_CHARGING) - @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - public boolean setWlcEnabled(boolean enable) { - if (!sHasNfcWlcFeature) { - throw new UnsupportedOperationException(); - } - return callServiceReturn(() -> sService.setWlcEnabled(enable), false); - } - - /** - * Checks NFC charging feature is enabled. - * - * @return True if NFC charging is enabled, false otherwise - * @throws UnsupportedOperationException if FEATURE_NFC_CHARGING - * is unavailable - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_CHARGING) - public boolean isWlcEnabled() { - if (!sHasNfcWlcFeature) { - throw new UnsupportedOperationException(); - } - return callServiceReturn(() -> sService.isWlcEnabled(), false); - - } - - /** - * A listener to be invoked when NFC controller always on state changes. - * <p>Register your {@code ControllerAlwaysOnListener} implementation with {@link - * NfcAdapter#registerWlcStateListener} and disable it with {@link - * NfcAdapter#unregisterWlcStateListenerListener}. - * @see #registerWlcStateListener - * @hide - */ - @SystemApi - @FlaggedApi(Flags.FLAG_ENABLE_NFC_CHARGING) - public interface WlcStateListener { - /** - * Called on NFC WLC state changes - */ - void onWlcStateChanged(@NonNull WlcListenerDeviceInfo wlcListenerDeviceInfo); - } - - /** - * Register a {@link WlcStateListener} to listen for NFC WLC state changes - * <p>The provided listener will be invoked by the given {@link Executor}. - * - * @param executor an {@link Executor} to execute given listener - * @param listener user implementation of the {@link WlcStateListener} - * @throws UnsupportedOperationException if FEATURE_NFC_CHARGING - * is unavailable - * - * @hide - */ - @SystemApi - @FlaggedApi(Flags.FLAG_ENABLE_NFC_CHARGING) - public void registerWlcStateListener( - @NonNull @CallbackExecutor Executor executor, - @NonNull WlcStateListener listener) { - if (!sHasNfcWlcFeature) { - throw new UnsupportedOperationException(); - } - mNfcWlcStateListener.register(executor, listener); - } - - /** - * Unregister the specified {@link WlcStateListener} - * <p>The same {@link WlcStateListener} object used when calling - * {@link #registerWlcStateListener(Executor, WlcStateListener)} - * must be used. - * - * <p>Listeners are automatically unregistered when application process goes away - * - * @param listener user implementation of the {@link WlcStateListener}a - * @throws UnsupportedOperationException if FEATURE_NFC_CHARGING - * is unavailable - * - * @hide - */ - @SystemApi - @FlaggedApi(Flags.FLAG_ENABLE_NFC_CHARGING) - public void unregisterWlcStateListener( - @NonNull WlcStateListener listener) { - if (!sHasNfcWlcFeature) { - throw new UnsupportedOperationException(); - } - mNfcWlcStateListener.unregister(listener); - } - - /** - * Returns information on the NFC charging listener device - * - * @return Information on the NFC charging listener device - * @throws UnsupportedOperationException if FEATURE_NFC_CHARGING - * is unavailable - */ - @FlaggedApi(Flags.FLAG_ENABLE_NFC_CHARGING) - @Nullable - public WlcListenerDeviceInfo getWlcListenerDeviceInfo() { - if (!sHasNfcWlcFeature) { - throw new UnsupportedOperationException(); - } - return callServiceReturn(() -> sService.getWlcListenerDeviceInfo(), null); - - } - - /** - * Vendor NCI command success. - * @hide - */ - @SystemApi - @FlaggedApi(Flags.FLAG_NFC_VENDOR_CMD) - public static final int SEND_VENDOR_NCI_STATUS_SUCCESS = 0; - /** - * Vendor NCI command rejected. - * @hide - */ - @SystemApi - @FlaggedApi(Flags.FLAG_NFC_VENDOR_CMD) - public static final int SEND_VENDOR_NCI_STATUS_REJECTED = 1; - /** - * Vendor NCI command corrupted. - * @hide - */ - @SystemApi - @FlaggedApi(Flags.FLAG_NFC_VENDOR_CMD) - public static final int SEND_VENDOR_NCI_STATUS_MESSAGE_CORRUPTED = 2; - /** - * Vendor NCI command failed with unknown reason. - * @hide - */ - @SystemApi - @FlaggedApi(Flags.FLAG_NFC_VENDOR_CMD) - public static final int SEND_VENDOR_NCI_STATUS_FAILED = 3; - - /** - * @hide - */ - @Retention(RetentionPolicy.SOURCE) - @IntDef(value = { - SEND_VENDOR_NCI_STATUS_SUCCESS, - SEND_VENDOR_NCI_STATUS_REJECTED, - SEND_VENDOR_NCI_STATUS_MESSAGE_CORRUPTED, - SEND_VENDOR_NCI_STATUS_FAILED, - }) - @interface SendVendorNciStatus {} - - /** - * Message Type for NCI Command. - * @hide - */ - @SystemApi - @FlaggedApi(Flags.FLAG_NFC_VENDOR_CMD) - public static final int MESSAGE_TYPE_COMMAND = 1; - - /** - * @hide - */ - @Retention(RetentionPolicy.SOURCE) - @IntDef(value = { - MESSAGE_TYPE_COMMAND, - }) - @interface MessageType {} - - /** - * Send Vendor specific Nci Messages with custom message type. - * - * The format of the NCI messages are defined in the NCI specification. The platform is - * responsible for fragmenting the payload if necessary. - * - * Note that mt (message type) is added at the beginning of method parameters as it is more - * distinctive than other parameters and was requested from vendor. - * - * @param mt message Type of the command - * @param gid group ID of the command. This needs to be one of the vendor reserved GIDs from - * the NCI specification - * @param oid opcode ID of the command. This is left to the OEM / vendor to decide - * @param payload containing vendor Nci message payload - * @return message send status - * @hide - */ - @SystemApi - @FlaggedApi(Flags.FLAG_NFC_VENDOR_CMD) - @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - public @SendVendorNciStatus int sendVendorNciMessage(@MessageType int mt, - @IntRange(from = 0, to = 15) int gid, @IntRange(from = 0) int oid, - @NonNull byte[] payload) { - Objects.requireNonNull(payload, "Payload must not be null"); - return callServiceReturn(() -> sService.sendVendorNciMessage(mt, gid, oid, payload), - SEND_VENDOR_NCI_STATUS_FAILED); - } - - /** - * Register an {@link NfcVendorNciCallback} to listen for Nfc vendor responses and notifications - * <p>The provided callback will be invoked by the given {@link Executor}. - * - * <p>When first registering a callback, the callbacks's - * {@link NfcVendorNciCallback#onVendorNciCallBack(byte[])} is immediately invoked to - * notify the vendor notification. - * - * @param executor an {@link Executor} to execute given callback - * @param callback user implementation of the {@link NfcVendorNciCallback} - * @hide - */ - @SystemApi - @FlaggedApi(Flags.FLAG_NFC_VENDOR_CMD) - @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - public void registerNfcVendorNciCallback(@NonNull @CallbackExecutor Executor executor, - @NonNull NfcVendorNciCallback callback) { - mNfcVendorNciCallbackListener.register(executor, callback); - } - - /** - * Unregister the specified {@link NfcVendorNciCallback} - * - * <p>The same {@link NfcVendorNciCallback} object used when calling - * {@link #registerNfcVendorNciCallback(Executor, NfcVendorNciCallback)} must be used. - * - * <p>Callbacks are automatically unregistered when application process goes away - * - * @param callback user implementation of the {@link NfcVendorNciCallback} - * @hide - */ - @SystemApi - @FlaggedApi(Flags.FLAG_NFC_VENDOR_CMD) - @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - public void unregisterNfcVendorNciCallback(@NonNull NfcVendorNciCallback callback) { - mNfcVendorNciCallbackListener.unregister(callback); - } - - /** - * Interface for receiving vendor NCI responses and notifications. - * @hide - */ - @SystemApi - @FlaggedApi(Flags.FLAG_NFC_VENDOR_CMD) - public interface NfcVendorNciCallback { - /** - * Invoked when a vendor specific NCI response is received. - * - * @param gid group ID of the command. This needs to be one of the vendor reserved GIDs from - * the NCI specification. - * @param oid opcode ID of the command. This is left to the OEM / vendor to decide. - * @param payload containing vendor Nci message payload. - */ - @FlaggedApi(Flags.FLAG_NFC_VENDOR_CMD) - void onVendorNciResponse( - @IntRange(from = 0, to = 15) int gid, int oid, @NonNull byte[] payload); - - /** - * Invoked when a vendor specific NCI notification is received. - * - * @param gid group ID of the command. This needs to be one of the vendor reserved GIDs from - * the NCI specification. - * @param oid opcode ID of the command. This is left to the OEM / vendor to decide. - * @param payload containing vendor Nci message payload. - */ - @FlaggedApi(Flags.FLAG_NFC_VENDOR_CMD) - void onVendorNciNotification( - @IntRange(from = 9, to = 15) int gid, int oid, @NonNull byte[] payload); - } - - /** - * Used by data migration to indicate data migration is in progrerss or not. - * - * Note: This is @hide intentionally since the client is inside the NFC apex. - * @param inProgress true if migration is in progress, false once done. - * @hide - */ - @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - public void indicateDataMigration(boolean inProgress) { - callService(() -> sService.indicateDataMigration(inProgress, mContext.getPackageName())); - } - - /** - * Returns an instance of {@link NfcOemExtension} associated with {@link NfcAdapter} instance. - * @hide - */ - @SystemApi - @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) - @NonNull public NfcOemExtension getNfcOemExtension() { - synchronized (sLock) { - if (!sHasNfcFeature) { - throw new UnsupportedOperationException(); - } - } - return mNfcOemExtension; - } - - /** - * Activity action: Bring up the settings page that allows the user to enable or disable tag - * intent reception for apps. - * - * <p>This will direct user to the settings page shows a list that asks users whether - * they want to allow or disallow the package to start an activity when a tag is discovered. - * - */ - @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) - @FlaggedApi(Flags.FLAG_NFC_CHECK_TAG_INTENT_PREFERENCE) - public static final String ACTION_CHANGE_TAG_INTENT_PREFERENCE = - "android.nfc.action.CHANGE_TAG_INTENT_PREFERENCE"; - - /** - * Checks whether the user has disabled the calling app from receiving NFC tag intents. - * - * <p>This method checks whether the caller package name is either not present in the user - * disabled list or is explicitly allowed by the user. - * - * @return {@code true} if an app is either not present in the list or is added to the list - * with the flag set to {@code true}. Otherwise, it returns {@code false}. - * It also returns {@code true} if {@link isTagIntentAppPreferenceSupported} returns - * {@code false}. - * - * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. - */ - @FlaggedApi(Flags.FLAG_NFC_CHECK_TAG_INTENT_PREFERENCE) - public boolean isTagIntentAllowed() { - if (!sHasNfcFeature) { - throw new UnsupportedOperationException(); - } - if (!isTagIntentAppPreferenceSupported()) { - return true; - } - return callServiceReturn(() -> sService.isTagIntentAllowed(mContext.getPackageName(), - UserHandle.myUserId()), false); - } -} diff --git a/nfc/java/android/nfc/NfcAntennaInfo.aidl b/nfc/java/android/nfc/NfcAntennaInfo.aidl deleted file mode 100644 index d5e79fc37282..000000000000 --- a/nfc/java/android/nfc/NfcAntennaInfo.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2013 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; - -parcelable NfcAntennaInfo; diff --git a/nfc/java/android/nfc/NfcAntennaInfo.java b/nfc/java/android/nfc/NfcAntennaInfo.java deleted file mode 100644 index c57b2e029cc5..000000000000 --- a/nfc/java/android/nfc/NfcAntennaInfo.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (C) 2015 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.NonNull; -import android.os.Parcel; -import android.os.Parcelable; - -import java.util.ArrayList; -import java.util.List; - - -/** - * Contains information on all available Nfc - * antennas on an Android device as well as information - * on the device itself in relation positioning of the - * antennas. - */ -public final class NfcAntennaInfo implements Parcelable { - // Width of the device in millimeters. - private final int mDeviceWidth; - // Height of the device in millimeters. - private final int mDeviceHeight; - // Whether the device is foldable. - private final boolean mDeviceFoldable; - // All available Nfc Antennas on the device. - private final List<AvailableNfcAntenna> mAvailableNfcAntennas; - - public NfcAntennaInfo(int deviceWidth, int deviceHeight, boolean deviceFoldable, - @NonNull List<AvailableNfcAntenna> availableNfcAntennas) { - this.mDeviceWidth = deviceWidth; - this.mDeviceHeight = deviceHeight; - this.mDeviceFoldable = deviceFoldable; - this.mAvailableNfcAntennas = availableNfcAntennas; - } - - /** - * Width of the device in millimeters. - */ - public int getDeviceWidth() { - return mDeviceWidth; - } - - /** - * Height of the device in millimeters. - */ - public int getDeviceHeight() { - return mDeviceHeight; - } - - /** - * Whether the device is foldable. When the device is foldable, - * the 0, 0 is considered to be top-left when the device is unfolded and - * the screens are facing the user. For non-foldable devices 0, 0 - * is top-left when the user is facing the screen. - */ - public boolean isDeviceFoldable() { - return mDeviceFoldable; - } - - /** - * Get all NFC antennas that exist on the device. - */ - @NonNull - public List<AvailableNfcAntenna> getAvailableNfcAntennas() { - return mAvailableNfcAntennas; - } - - private NfcAntennaInfo(Parcel in) { - this.mDeviceWidth = in.readInt(); - this.mDeviceHeight = in.readInt(); - this.mDeviceFoldable = in.readByte() != 0; - this.mAvailableNfcAntennas = new ArrayList<>(); - in.readTypedList(this.mAvailableNfcAntennas, - AvailableNfcAntenna.CREATOR); - } - - public static final @NonNull Parcelable.Creator<NfcAntennaInfo> CREATOR = - new Parcelable.Creator<NfcAntennaInfo>() { - @Override - public NfcAntennaInfo createFromParcel(Parcel in) { - return new NfcAntennaInfo(in); - } - - @Override - public NfcAntennaInfo[] newArray(int size) { - return new NfcAntennaInfo[size]; - } - }; - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeInt(mDeviceWidth); - dest.writeInt(mDeviceHeight); - dest.writeByte((byte) (mDeviceFoldable ? 1 : 0)); - dest.writeTypedList(mAvailableNfcAntennas, 0); - } -} diff --git a/nfc/java/android/nfc/NfcControllerAlwaysOnListener.java b/nfc/java/android/nfc/NfcControllerAlwaysOnListener.java deleted file mode 100644 index 6ae58fd38cbe..000000000000 --- a/nfc/java/android/nfc/NfcControllerAlwaysOnListener.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright 2021 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.NonNull; -import android.nfc.NfcAdapter.ControllerAlwaysOnListener; -import android.os.Binder; -import android.os.RemoteException; -import android.util.Log; - -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.Executor; - -/** - * @hide - */ -public class NfcControllerAlwaysOnListener extends INfcControllerAlwaysOnListener.Stub { - private static final String TAG = NfcControllerAlwaysOnListener.class.getSimpleName(); - - private final INfcAdapter mAdapter; - - private final Map<ControllerAlwaysOnListener, Executor> mListenerMap = new HashMap<>(); - - private boolean mCurrentState = false; - private boolean mIsRegistered = false; - - public NfcControllerAlwaysOnListener(@NonNull INfcAdapter adapter) { - mAdapter = adapter; - } - - /** - * Register a {@link ControllerAlwaysOnListener} with this - * {@link NfcControllerAlwaysOnListener} - * - * @param executor an {@link Executor} to execute given listener - * @param listener user implementation of the {@link ControllerAlwaysOnListener} - */ - public void register(@NonNull Executor executor, - @NonNull ControllerAlwaysOnListener listener) { - try { - if (!mAdapter.isControllerAlwaysOnSupported()) { - return; - } - } catch (RemoteException e) { - Log.w(TAG, "Failed to register"); - return; - } - synchronized (this) { - if (mListenerMap.containsKey(listener)) { - return; - } - - mListenerMap.put(listener, executor); - if (!mIsRegistered) { - try { - mAdapter.registerControllerAlwaysOnListener(this); - mIsRegistered = true; - } catch (RemoteException e) { - Log.w(TAG, "Failed to register"); - } - } - } - } - - /** - * Unregister the specified {@link ControllerAlwaysOnListener} - * - * @param listener user implementation of the {@link ControllerAlwaysOnListener} - */ - public void unregister(@NonNull ControllerAlwaysOnListener listener) { - try { - if (!mAdapter.isControllerAlwaysOnSupported()) { - return; - } - } catch (RemoteException e) { - Log.w(TAG, "Failed to unregister"); - return; - } - synchronized (this) { - if (!mListenerMap.containsKey(listener)) { - return; - } - - mListenerMap.remove(listener); - - if (mListenerMap.isEmpty() && mIsRegistered) { - try { - mAdapter.unregisterControllerAlwaysOnListener(this); - } catch (RemoteException e) { - Log.w(TAG, "Failed to unregister"); - } - mIsRegistered = false; - } - } - } - - private void sendCurrentState(@NonNull ControllerAlwaysOnListener listener) { - synchronized (this) { - Executor executor = mListenerMap.get(listener); - - final long identity = Binder.clearCallingIdentity(); - try { - executor.execute(() -> listener.onControllerAlwaysOnChanged( - mCurrentState)); - } finally { - Binder.restoreCallingIdentity(identity); - } - } - } - - @Override - public void onControllerAlwaysOnChanged(boolean isEnabled) { - synchronized (this) { - mCurrentState = isEnabled; - for (ControllerAlwaysOnListener cb : mListenerMap.keySet()) { - sendCurrentState(cb); - } - } - } -} - diff --git a/nfc/java/android/nfc/NfcEvent.java b/nfc/java/android/nfc/NfcEvent.java deleted file mode 100644 index aff4f52f2bab..000000000000 --- a/nfc/java/android/nfc/NfcEvent.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2011 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; - -/** - * Wraps information associated with any NFC event. - * - * <p>Immutable object, with direct access to the (final) fields. - * - * <p>An {@link NfcEvent} object is usually included in callbacks from - * {@link NfcAdapter}. Check the documentation of the callback to see - * which fields may be set. - * - * <p>This wrapper object is used (instead of parameters - * in the callback) because it allows new fields to be added without breaking - * API compatibility. - * - * @see NfcAdapter.OnNdefPushCompleteCallback#onNdefPushComplete - * @see NfcAdapter.CreateNdefMessageCallback#createNdefMessage - */ -public final class NfcEvent { - /** - * The {@link NfcAdapter} associated with the NFC event. - */ - public final NfcAdapter nfcAdapter; - - /** - * The major LLCP version number of the peer associated with the NFC event. - */ - public final int peerLlcpMajorVersion; - - /** - * The minor LLCP version number of the peer associated with the NFC event. - */ - public final int peerLlcpMinorVersion; - - NfcEvent(NfcAdapter nfcAdapter, byte peerLlcpVersion) { - this.nfcAdapter = nfcAdapter; - this.peerLlcpMajorVersion = (peerLlcpVersion & 0xF0) >> 4; - this.peerLlcpMinorVersion = peerLlcpVersion & 0x0F; - } -} diff --git a/nfc/java/android/nfc/NfcFrameworkInitializer.java b/nfc/java/android/nfc/NfcFrameworkInitializer.java deleted file mode 100644 index 1ab8a1ebd72c..000000000000 --- a/nfc/java/android/nfc/NfcFrameworkInitializer.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * 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; - -import android.annotation.NonNull; -import android.annotation.SystemApi; -import android.app.SystemServiceRegistry; -import android.content.Context; - -/** - * Class for performing registration for Nfc service. - * - * @hide - */ -@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) -public class NfcFrameworkInitializer { - private NfcFrameworkInitializer() {} - - private static volatile NfcServiceManager sNfcServiceManager; - - /** - * Sets an instance of {@link NfcServiceManager} that allows - * the nfc mainline module to register/obtain nfc binder services. This is called - * by the platform during the system initialization. - * - * @param nfcServiceManager instance of {@link NfcServiceManager} that allows - * the nfc mainline module to register/obtain nfcd binder services. - */ - public static void setNfcServiceManager( - @NonNull NfcServiceManager nfcServiceManager) { - if (sNfcServiceManager != null) { - throw new IllegalStateException("setNfcServiceManager called twice!"); - } - - if (nfcServiceManager == null) { - throw new IllegalArgumentException("nfcServiceManager must not be null"); - } - - sNfcServiceManager = nfcServiceManager; - } - - /** @hide */ - public static NfcServiceManager getNfcServiceManager() { - return sNfcServiceManager; - } - - /** - * Called by {@link SystemServiceRegistry}'s static initializer and registers NFC service - * to {@link Context}, so that {@link Context#getSystemService} can return them. - * - * @throws IllegalStateException if this is called from anywhere besides - * {@link SystemServiceRegistry} - */ - public static void registerServiceWrappers() { - SystemServiceRegistry.registerContextAwareService(Context.NFC_SERVICE, - NfcManager.class, context -> new NfcManager(context)); - } -} diff --git a/nfc/java/android/nfc/NfcManager.java b/nfc/java/android/nfc/NfcManager.java deleted file mode 100644 index 644e3122774b..000000000000 --- a/nfc/java/android/nfc/NfcManager.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) 2010 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.SystemService; -import android.compat.annotation.UnsupportedAppUsage; -import android.content.Context; -import android.os.Build; - -/** - * High level manager used to obtain an instance of an {@link NfcAdapter}. - * <p> - * Use {@link android.content.Context#getSystemService(java.lang.String)} - * with {@link Context#NFC_SERVICE} to create an {@link NfcManager}, - * then call {@link #getDefaultAdapter} to obtain the {@link NfcAdapter}. - * <p> - * Alternately, you can just call the static helper - * {@link NfcAdapter#getDefaultAdapter(android.content.Context)}. - * - * <div class="special reference"> - * <h3>Developer Guides</h3> - * <p>For more information about using NFC, read the - * <a href="{@docRoot}guide/topics/nfc/index.html">Near Field Communication</a> developer guide.</p> - * </div> - * - * @see NfcAdapter#getDefaultAdapter(android.content.Context) - */ -@SystemService(Context.NFC_SERVICE) -public final class NfcManager { - private final NfcAdapter mAdapter; - - /** - * @hide - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) - public NfcManager(Context context) { - NfcAdapter adapter; - context = context.getApplicationContext(); - if (context == null) { - throw new IllegalArgumentException( - "context not associated with any application (using a mock context?)"); - } - try { - adapter = NfcAdapter.getNfcAdapter(context); - } catch (UnsupportedOperationException e) { - adapter = null; - } - mAdapter = adapter; - } - - /** - * Get the default NFC Adapter for this device. - * - * @return the default NFC Adapter - */ - public NfcAdapter getDefaultAdapter() { - return mAdapter; - } -} diff --git a/nfc/java/android/nfc/NfcOemExtension.java b/nfc/java/android/nfc/NfcOemExtension.java deleted file mode 100644 index 1fc0786d3582..000000000000 --- a/nfc/java/android/nfc/NfcOemExtension.java +++ /dev/null @@ -1,1248 +0,0 @@ -/* - * Copyright (C) 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 static android.nfc.cardemulation.CardEmulation.PROTOCOL_AND_TECHNOLOGY_ROUTE_DH; -import static android.nfc.cardemulation.CardEmulation.PROTOCOL_AND_TECHNOLOGY_ROUTE_ESE; -import static android.nfc.cardemulation.CardEmulation.PROTOCOL_AND_TECHNOLOGY_ROUTE_UICC; -import static android.nfc.cardemulation.CardEmulation.routeIntToString; - -import android.Manifest; -import android.annotation.CallbackExecutor; -import android.annotation.DurationMillisLong; -import android.annotation.FlaggedApi; -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.annotation.RequiresPermission; -import android.annotation.SystemApi; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.nfc.cardemulation.ApduServiceInfo; -import android.nfc.cardemulation.CardEmulation; -import android.nfc.cardemulation.CardEmulation.ProtocolAndTechnologyRoute; -import android.os.Binder; -import android.os.Bundle; -import android.os.RemoteException; -import android.os.ResultReceiver; -import android.se.omapi.Reader; -import android.util.Log; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executor; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.FutureTask; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.function.BiConsumer; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; - -/** - * Used for OEM extension APIs. - * This class holds all the APIs and callbacks defined for OEMs/vendors to extend the NFC stack - * for their proprietary features. - * - * @hide - */ -@FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) -@SystemApi -public final class NfcOemExtension { - private static final String TAG = "NfcOemExtension"; - private static final int OEM_EXTENSION_RESPONSE_THRESHOLD_MS = 2000; - private static final int TYPE_TECHNOLOGY = 0; - private static final int TYPE_PROTOCOL = 1; - private static final int TYPE_AID = 2; - private static final int TYPE_SYSTEMCODE = 3; - - private final NfcAdapter mAdapter; - private final NfcOemExtensionCallback mOemNfcExtensionCallback; - private boolean mIsRegistered = false; - private final Map<Callback, Executor> mCallbackMap = new HashMap<>(); - private final Context mContext; - private final Object mLock = new Object(); - private boolean mCardEmulationActivated = false; - private boolean mRfFieldActivated = false; - private boolean mRfDiscoveryStarted = false; - private boolean mEeListenActivated = false; - - /** - * Broadcast Action: Sent on NFC stack initialization when NFC OEM extensions are enabled. - * <p> OEM extension modules should use this intent to start their extension service </p> - * @hide - */ - public static final String ACTION_OEM_EXTENSION_INIT = "android.nfc.action.OEM_EXTENSION_INIT"; - - /** - * Mode Type for {@link #setControllerAlwaysOnMode(int)}. - * Enables the controller in default mode when NFC is disabled (existing API behavior). - * works same as {@link NfcAdapter#setControllerAlwaysOn(boolean)}. - * @hide - */ - @SystemApi - @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) - public static final int ENABLE_DEFAULT = NfcAdapter.CONTROLLER_ALWAYS_ON_MODE_DEFAULT; - - /** - * Mode Type for {@link #setControllerAlwaysOnMode(int)}. - * Enables the controller in transparent mode when NFC is disabled. - * @hide - */ - @SystemApi - @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) - public static final int ENABLE_TRANSPARENT = 2; - - /** - * Mode Type for {@link #setControllerAlwaysOnMode(int)}. - * Enables the controller and initializes and enables the EE subsystem when NFC is disabled. - * @hide - */ - @SystemApi - @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) - public static final int ENABLE_EE = 3; - - /** - * Mode Type for {@link #setControllerAlwaysOnMode(int)}. - * Disable the Controller Always On Mode. - * works same as {@link NfcAdapter#setControllerAlwaysOn(boolean)}. - * @hide - */ - @SystemApi - @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) - public static final int DISABLE = NfcAdapter.CONTROLLER_ALWAYS_ON_DISABLE; - - /** - * Possible controller modes for {@link #setControllerAlwaysOnMode(int)}. - * - * @hide - */ - @IntDef(prefix = { "" }, value = { - ENABLE_DEFAULT, - ENABLE_TRANSPARENT, - ENABLE_EE, - DISABLE, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface ControllerMode{} - - /** - * Technology Type for {@link #getActiveNfceeList()}. - */ - @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) - public static final int NFCEE_TECH_NONE = 0; - - /** - * Technology Type for {@link #getActiveNfceeList()}. - */ - @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) - public static final int NFCEE_TECH_A = 1; - - /** - * Technology Type for {@link #getActiveNfceeList()}. - */ - @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) - public static final int NFCEE_TECH_B = 1 << 1; - - /** - * Technology Type for {@link #getActiveNfceeList()}. - */ - @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) - public static final int NFCEE_TECH_F = 1 << 2; - - /** - * Nfc technology flags for {@link #getActiveNfceeList()}. - * - * @hide - */ - @IntDef(flag = true, value = { - NFCEE_TECH_NONE, - NFCEE_TECH_A, - NFCEE_TECH_B, - NFCEE_TECH_F, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface NfceeTechnology {} - - /** - * Event that Host Card Emulation is activated. - */ - public static final int HCE_ACTIVATE = 1; - /** - * Event that some data is transferred in Host Card Emulation. - */ - public static final int HCE_DATA_TRANSFERRED = 2; - /** - * Event that Host Card Emulation is deactivated. - */ - public static final int HCE_DEACTIVATE = 3; - /** - * Possible events from {@link Callback#onHceEventReceived}. - * - * @hide - */ - @IntDef(value = { - HCE_ACTIVATE, - HCE_DATA_TRANSFERRED, - HCE_DEACTIVATE - }) - @Retention(RetentionPolicy.SOURCE) - public @interface HostCardEmulationAction {} - - /** - * Status code returned when the polling state change request succeeded. - * @see #pausePolling() - * @see #resumePolling() - */ - public static final int POLLING_STATE_CHANGE_SUCCEEDED = 1; - /** - * Status code returned when the polling state change request is already in - * required state. - * @see #pausePolling() - * @see #resumePolling() - */ - public static final int POLLING_STATE_CHANGE_ALREADY_IN_REQUESTED_STATE = 2; - /** - * Possible status codes for {@link #pausePolling()} and - * {@link #resumePolling()}. - * @hide - */ - @IntDef(value = { - POLLING_STATE_CHANGE_SUCCEEDED, - POLLING_STATE_CHANGE_ALREADY_IN_REQUESTED_STATE, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface PollingStateChangeStatusCode {} - - /** - * Status OK - */ - public static final int STATUS_OK = 0; - /** - * Status unknown error - */ - public static final int STATUS_UNKNOWN_ERROR = 1; - - /** - * Status codes passed to OEM extension callbacks. - * - * @hide - */ - @IntDef(value = { - STATUS_OK, - STATUS_UNKNOWN_ERROR - }) - @Retention(RetentionPolicy.SOURCE) - public @interface StatusCode {} - - /** - * Routing commit succeeded. - */ - public static final int COMMIT_ROUTING_STATUS_OK = 0; - /** - * Routing commit failed. - */ - public static final int COMMIT_ROUTING_STATUS_FAILED = 3; - /** - * Routing commit failed due to the update is in progress. - */ - public static final int COMMIT_ROUTING_STATUS_FAILED_UPDATE_IN_PROGRESS = 6; - - /** - * Status codes returned when calling {@link #forceRoutingTableCommit()} - * @hide - */ - @IntDef(prefix = "COMMIT_ROUTING_STATUS_", value = { - COMMIT_ROUTING_STATUS_OK, - COMMIT_ROUTING_STATUS_FAILED, - COMMIT_ROUTING_STATUS_FAILED_UPDATE_IN_PROGRESS, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface CommitRoutingStatusCode {} - /** - * Interface for Oem extensions for NFC. - */ - public interface Callback { - /** - * Notify Oem to tag is connected or not - * ex - if tag is connected notify cover and Nfctest app if app is in testing mode - * - * @param connected status of the tag true if tag is connected otherwise false - */ - void onTagConnected(boolean connected); - - /** - * Update the Nfc Adapter State - * @param state new state that need to be updated - */ - void onStateUpdated(@NfcAdapter.AdapterState int state); - /** - * Check if NfcService apply routing method need to be skipped for - * some feature. - * @param isSkipped The {@link Consumer} to be completed. If apply routing can be skipped, - * the {@link Consumer#accept(Object)} should be called with - * {@link Boolean#TRUE}, otherwise call with {@link Boolean#FALSE}. - */ - void onApplyRouting(@NonNull Consumer<Boolean> isSkipped); - /** - * Check if NfcService ndefRead method need to be skipped To skip - * and start checking for presence of tag - * @param isSkipped The {@link Consumer} to be completed. If Ndef read can be skipped, - * the {@link Consumer#accept(Object)} should be called with - * {@link Boolean#TRUE}, otherwise call with {@link Boolean#FALSE}. - */ - void onNdefRead(@NonNull Consumer<Boolean> isSkipped); - /** - * Method to check if Nfc is allowed to be enabled by OEMs. - * @param isAllowed The {@link Consumer} to be completed. If enabling NFC is allowed, - * the {@link Consumer#accept(Object)} should be called with - * {@link Boolean#TRUE}, otherwise call with {@link Boolean#FALSE}. - * false if NFC cannot be enabled at this time. - */ - void onEnableRequested(@NonNull Consumer<Boolean> isAllowed); - /** - * Method to check if Nfc is allowed to be disabled by OEMs. - * @param isAllowed The {@link Consumer} to be completed. If disabling NFC is allowed, - * the {@link Consumer#accept(Object)} should be called with - * {@link Boolean#TRUE}, otherwise call with {@link Boolean#FALSE}. - * false if NFC cannot be disabled at this time. - */ - void onDisableRequested(@NonNull Consumer<Boolean> isAllowed); - - /** - * Callback to indicate that Nfc starts to boot. - */ - void onBootStarted(); - - /** - * Callback to indicate that Nfc starts to enable. - */ - void onEnableStarted(); - - /** - * Callback to indicate that Nfc starts to disable. - */ - void onDisableStarted(); - - /** - * Callback to indicate if NFC boots successfully or not. - * @param status the status code indicating if boot finished successfully - */ - void onBootFinished(@StatusCode int status); - - /** - * Callback to indicate if NFC is successfully enabled. - * @param status the status code indicating if enable finished successfully - */ - void onEnableFinished(@StatusCode int status); - - /** - * Callback to indicate if NFC is successfully disabled. - * @param status the status code indicating if disable finished successfully - */ - void onDisableFinished(@StatusCode int status); - - /** - * Check if NfcService tag dispatch need to be skipped. - * @param isSkipped The {@link Consumer} to be completed. If tag dispatch can be skipped, - * the {@link Consumer#accept(Object)} should be called with - * {@link Boolean#TRUE}, otherwise call with {@link Boolean#FALSE}. - */ - void onTagDispatch(@NonNull Consumer<Boolean> isSkipped); - - /** - * Notifies routing configuration is changed. - * @param isCommitRoutingSkipped The {@link Consumer} to be - * completed. If routing commit should be skipped, - * the {@link Consumer#accept(Object)} should be called with - * {@link Boolean#TRUE}, otherwise call with {@link Boolean#FALSE}. - */ - void onRoutingChanged(@NonNull Consumer<Boolean> isCommitRoutingSkipped); - - /** - * API to activate start stop cpu boost on hce event. - * - * <p>When HCE is activated, transferring data, and deactivated, - * must call this method to activate, start and stop cpu boost respectively. - * @param action Flag indicating actions to activate, start and stop cpu boost. - */ - void onHceEventReceived(@HostCardEmulationAction int action); - - /** - * API to notify when reader option has been changed using - * {@link NfcAdapter#enableReaderOption(boolean)} by some app. - * @param enabled Flag indicating ReaderMode enabled/disabled - */ - void onReaderOptionChanged(boolean enabled); - - /** - * Notifies NFC is activated in listen mode. - * NFC Forum NCI-2.3 ch.5.2.6 specification - * - * <p>NFCC is ready to communicate with a Card reader - * - * @param isActivated true, if card emulation activated, else de-activated. - */ - void onCardEmulationActivated(boolean isActivated); - - /** - * Notifies the Remote NFC Endpoint RF Field is detected. - * NFC Forum NCI-2.3 ch.5.3 specification - * - * @param isActive true, if RF Field is ON, else RF Field is OFF. - */ - void onRfFieldDetected(boolean isActive); - - /** - * Notifies the NFC RF discovery is started or in the IDLE state. - * NFC Forum NCI-2.3 ch.5.2 specification - * - * @param isDiscoveryStarted true, if RF discovery started, else RF state is Idle. - */ - void onRfDiscoveryStarted(boolean isDiscoveryStarted); - - /** - * Notifies the NFCEE (NFC Execution Environment) Listen has been activated. - * - * @param isActivated true, if EE Listen is ON, else EE Listen is OFF. - */ - void onEeListenActivated(boolean isActivated); - - /** - * Notifies that some NFCEE (NFC Execution Environment) has been updated. - * - * <p> This indicates that some applet has been installed/updated/removed in - * one of the NFCEE's. - * </p> - */ - void onEeUpdated(); - - /** - * Gets the intent to find the OEM package in the OEM App market. If the consumer returns - * {@code null} or a timeout occurs, the intent from the first available package will be - * used instead. - * - * @param packages the OEM packages name stored in the tag - * @param intentConsumer The {@link Consumer} to be completed. - * The {@link Consumer#accept(Object)} should be called with - * the Intent required. - * - */ - void onGetOemAppSearchIntent(@NonNull List<String> packages, - @NonNull Consumer<Intent> intentConsumer); - - /** - * Checks if the NDEF message contains any specific OEM package executable content - * - * @param tag the {@link android.nfc.Tag Tag} - * @param message NDEF Message to read from tag - * @param hasOemExecutableContent The {@link Consumer} to be completed. If there is - * OEM package executable content, the - * {@link Consumer#accept(Object)} should be called with - * {@link Boolean#TRUE}, otherwise call with - * {@link Boolean#FALSE}. - */ - void onNdefMessage(@NonNull Tag tag, @NonNull NdefMessage message, - @NonNull Consumer<Boolean> hasOemExecutableContent); - - /** - * Callback to indicate the app chooser activity should be launched for handling CE - * transaction. This is invoked for example when there are more than 1 app installed that - * can handle the HCE transaction. OEMs can launch the Activity based - * on their requirement. - * - * @param selectedAid the selected AID from APDU - * @param services {@link ApduServiceInfo} of the service triggering the activity - * @param failedComponent the component failed to be resolved - * @param category the category of the service - */ - void onLaunchHceAppChooserActivity(@NonNull String selectedAid, - @NonNull List<ApduServiceInfo> services, - @NonNull ComponentName failedComponent, - @NonNull String category); - - /** - * Callback to indicate tap again dialog should be launched for handling HCE transaction. - * This is invoked for example when a CE service needs the device to unlocked before - * handling the transaction. OEMs can launch the Activity based on their requirement. - * - * @param service {@link ApduServiceInfo} of the service triggering the dialog - * @param category the category of the service - */ - void onLaunchHceTapAgainDialog(@NonNull ApduServiceInfo service, @NonNull String category); - - /** - * Callback to indicate that routing table is full and the OEM can optionally launch a - * dialog to request the user to remove some Card Emulation apps from the device to free - * routing table space. - */ - void onRoutingTableFull(); - - /** - * Callback when OEM specified log event are notified. - * @param item the log items that contains log information of NFC event. - */ - void onLogEventNotified(@NonNull OemLogItems item); - - /** - * Callback to to extract OEM defined packages from given NDEF message when - * a NFC tag is detected. These are used to handle NFC tags encoded with a - * proprietary format for storing app name (Android native app format). - * - * @param message NDEF message containing OEM package names - * @param packageConsumer The {@link Consumer} to be completed. - * The {@link Consumer#accept(Object)} should be called with - * the list of package names. - */ - void onExtractOemPackages(@NonNull NdefMessage message, - @NonNull Consumer<List<String>> packageConsumer); - } - - - /** - * Constructor to be used only by {@link NfcAdapter}. - */ - NfcOemExtension(@NonNull Context context, @NonNull NfcAdapter adapter) { - mContext = context; - mAdapter = adapter; - mOemNfcExtensionCallback = new NfcOemExtensionCallback(); - } - - /** - * Get an instance of {@link T4tNdefNfcee} object for performing T4T (Type-4 Tag) - * NDEF (NFC Data Exchange Format) NFCEE (NFC Execution Environment) operations. - * This can be used to write NDEF data to emulate a T4T tag in an NFCEE - * (NFC Execution Environment - eSE, SIM, etc). Refer to the NFC forum specification - * "NFCForum-TS-NCI-2.3 section 10.4" and "NFCForum-TS-T4T-1.1 section 4.2" for more details. - * - * This is a singleton object which shall be used by OEM extension module to do NDEF-NFCEE - * read/write operations. - * - * <p>Returns {@link T4tNdefNfcee} - * <p>Does not cause any RF activity and does not block. - * @return NFC Data Exchange Format (NDEF) NFC Execution Environment (NFCEE) object - * @hide - */ - @SystemApi - @NonNull - @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) - public T4tNdefNfcee getT4tNdefNfcee() { - return T4tNdefNfcee.getInstance(); - } - - /** - * Register an {@link Callback} to listen for NFC oem extension callbacks - * Multiple clients can register and callbacks will be invoked asynchronously. - * - * <p>The provided callback will be invoked by the given {@link Executor}. - * As part of {@link #registerCallback(Executor, Callback)} the - * {@link Callback} will be invoked with current NFC state - * before the {@link #registerCallback(Executor, Callback)} function completes. - * - * @param executor an {@link Executor} to execute given callback - * @param callback oem implementation of {@link Callback} - */ - @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) - @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - public void registerCallback(@NonNull @CallbackExecutor Executor executor, - @NonNull Callback callback) { - synchronized (mLock) { - if (executor == null || callback == null) { - Log.e(TAG, "Executor and Callback must not be null!"); - throw new IllegalArgumentException(); - } - - if (mCallbackMap.containsKey(callback)) { - Log.e(TAG, "Callback already registered. Unregister existing callback before" - + "registering"); - throw new IllegalArgumentException(); - } - mCallbackMap.put(callback, executor); - if (!mIsRegistered) { - NfcAdapter.callService(() -> { - NfcAdapter.sService.registerOemExtensionCallback(mOemNfcExtensionCallback); - mIsRegistered = true; - }); - } else { - updateNfCState(callback, executor); - } - } - } - - private void updateNfCState(Callback callback, Executor executor) { - if (callback != null) { - Log.i(TAG, "updateNfCState"); - executor.execute(() -> { - callback.onCardEmulationActivated(mCardEmulationActivated); - callback.onRfFieldDetected(mRfFieldActivated); - callback.onRfDiscoveryStarted(mRfDiscoveryStarted); - callback.onEeListenActivated(mEeListenActivated); - }); - } - } - - /** - * Unregister the specified {@link Callback} - * - * <p>The same {@link Callback} object used when calling - * {@link #registerCallback(Executor, Callback)} must be used. - * - * <p>Callbacks are automatically unregistered when an application process goes away - * - * @param callback oem implementation of {@link Callback} - */ - @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) - @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - public void unregisterCallback(@NonNull Callback callback) { - synchronized (mLock) { - if (!mCallbackMap.containsKey(callback) || !mIsRegistered) { - Log.e(TAG, "Callback not registered"); - throw new IllegalArgumentException(); - } - if (mCallbackMap.size() == 1) { - NfcAdapter.callService(() -> { - NfcAdapter.sService.unregisterOemExtensionCallback(mOemNfcExtensionCallback); - mIsRegistered = false; - mCallbackMap.remove(callback); - }); - } else { - mCallbackMap.remove(callback); - } - } - } - - /** - * Clear NfcService preference, interface method to clear NFC preference values on OEM specific - * events. For ex: on soft reset, Nfc default values needs to be overridden by OEM defaults. - */ - @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) - @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - public void clearPreference() { - NfcAdapter.callService(() -> NfcAdapter.sService.clearPreference()); - } - - /** - * Get the screen state from system and set it to current screen state. - */ - @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) - @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - public void synchronizeScreenState() { - NfcAdapter.callService(() -> NfcAdapter.sService.setScreenState()); - } - - /** - * Check if the firmware needs updating. - * - * <p>If an update is needed, a firmware will be triggered when NFC is disabled. - */ - @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) - @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - public void maybeTriggerFirmwareUpdate() { - NfcAdapter.callService(() -> NfcAdapter.sService.checkFirmware()); - } - - /** - * Get the Active NFCEE (NFC Execution Environment) List - * - * @return Map< String, @NfceeTechnology Integer > - * A HashMap where keys are activated secure elements and - * the values are bitmap of technologies supported by each secure element: - * NFCEE_TECH_A == 0x1 - * NFCEE_TECH_B == 0x2 - * NFCEE_TECH_F == 0x4 - * and keys can contain "eSE" and "SIM" with a number, - * in case of failure an empty map is returned. - * @see Reader#getName() for the list of possible NFCEE names. - */ - @NonNull - @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) - public Map<String, Integer> getActiveNfceeList() { - return NfcAdapter.callServiceReturn(() -> - NfcAdapter.sService.fetchActiveNfceeList(), new HashMap<String, Integer>()); - } - - /** - * Sets NFC controller always on feature. - * <p>This API is for the NFCC internal state management. It allows to discriminate - * the controller function from the NFC function by keeping the NFC controller on without - * any NFC RF enabled if necessary. - * <p>This call is asynchronous, register listener {@link NfcAdapter.ControllerAlwaysOnListener} - * by {@link NfcAdapter#registerControllerAlwaysOnListener} to find out when the operation is - * complete. - * <p> Note: This adds more always on modes on top of existing - * {@link NfcAdapter#setControllerAlwaysOn(boolean)} API which can be used to set the NFCC in - * only {@link #ENABLE_DEFAULT} and {@link #DISABLE} modes. - * @param mode one of {@link ControllerMode} modes - * @throws UnsupportedOperationException if - * <li> if FEATURE_NFC, FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF, - * FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC and FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE - * are unavailable </li> - * <li> if the feature is unavailable @see NfcAdapter#isNfcControllerAlwaysOnSupported() </li> - * @hide - * @see NfcAdapter#setControllerAlwaysOn(boolean) - */ - @SystemApi - @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) - @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) - public void setControllerAlwaysOnMode(@ControllerMode int mode) { - if (!NfcAdapter.sHasNfcFeature && !NfcAdapter.sHasCeFeature) { - throw new UnsupportedOperationException(); - } - NfcAdapter.callService(() -> NfcAdapter.sService.setControllerAlwaysOn(mode)); - } - - /** - * Triggers NFC initialization. If OEM extension is registered - * (indicated via `enable_oem_extension` NFC overlay), the NFC stack initialization at bootup - * is delayed until the OEM extension app triggers the initialization via this call. - */ - @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) - @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - public void triggerInitialization() { - NfcAdapter.callService(() -> NfcAdapter.sService.triggerInitialization()); - } - - /** - * Gets the last user toggle status. - * @return true if NFC is set to ON, false otherwise - */ - @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) - @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - public boolean hasUserEnabledNfc() { - return NfcAdapter.callServiceReturn(() -> NfcAdapter.sService.getSettingStatus(), false); - } - - /** - * Checks if the tag is present or not. - * @return true if the tag is present, false otherwise - */ - @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) - @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - public boolean isTagPresent() { - return NfcAdapter.callServiceReturn(() -> NfcAdapter.sService.isTagPresent(), false); - } - - /** - * Pauses NFC tag reader mode polling for a {@code timeoutInMs} millisecond. - * In case of {@code timeoutInMs} is zero or invalid polling will be stopped indefinitely. - * Use {@link #resumePolling()} to resume the polling. - * Use {@link #getMaxPausePollingTimeoutMs()} to check the max timeout value. - * @param timeoutInMs the pause polling duration in millisecond. - * @return status of the operation - * @throws IllegalArgumentException if timeoutInMs value is invalid - * (0 < timeoutInMs < max). - */ - @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) - @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - public @PollingStateChangeStatusCode int pausePolling(@DurationMillisLong long timeoutInMs) { - return NfcAdapter.callServiceReturn(() -> - NfcAdapter.sService.pausePolling(timeoutInMs), - POLLING_STATE_CHANGE_ALREADY_IN_REQUESTED_STATE); - } - - /** - * Resumes default NFC tag reader mode polling for the current device state if polling is - * paused. Calling this while already in polling is a no-op. - * @return status of the operation - */ - @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) - @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - public @PollingStateChangeStatusCode int resumePolling() { - return NfcAdapter.callServiceReturn(() -> - NfcAdapter.sService.resumePolling(), - POLLING_STATE_CHANGE_ALREADY_IN_REQUESTED_STATE); - } - - /** - * Gets the max pause polling timeout value in millisecond. - * @return long integer representing the max timeout - */ - @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) - @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - @DurationMillisLong - public long getMaxPausePollingTimeoutMills() { - return NfcAdapter.callServiceReturn(() -> - NfcAdapter.sService.getMaxPausePollingTimeoutMs(), 0L); - } - - /** - * Set whether to enable auto routing change or not (enabled by default). - * If disabled, routing targets are limited to a single off-host destination. - * - * @param state status of auto routing change, true if enable, otherwise false - */ - @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) - @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS) - public void setAutoChangeEnabled(boolean state) { - NfcAdapter.callService(() -> - NfcAdapter.sCardEmulationService.setAutoChangeStatus(state)); - } - - /** - * Check if auto routing change is enabled or not. - * - * @return true if enabled, otherwise false - */ - @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) - @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS) - public boolean isAutoChangeEnabled() { - return NfcAdapter.callServiceReturn(() -> - NfcAdapter.sCardEmulationService.isAutoChangeEnabled(), false); - } - - /** - * Get current routing status - * - * @return {@link RoutingStatus} indicating the default route, default ISO-DEP - * route and default off-host route. - */ - @NonNull - @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) - @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS) - public RoutingStatus getRoutingStatus() { - List<String> status = NfcAdapter.callServiceReturn(() -> - NfcAdapter.sCardEmulationService.getRoutingStatus(), new ArrayList<>()); - return new RoutingStatus(routeStringToInt(status.get(0)), - routeStringToInt(status.get(1)), - routeStringToInt(status.get(2))); - } - - /** - * Overwrites NFC controller routing table, which includes Protocol Route, Technology Route, - * and Empty AID Route. - * - * The parameter set to - * {@link ProtocolAndTechnologyRoute#PROTOCOL_AND_TECHNOLOGY_ROUTE_UNSET} - * can be used to keep current values for that entry. At least one route should be overridden - * when calling this API, otherwise throw {@link IllegalArgumentException}. - * - * @param protocol ISO-DEP route destination, where the possible inputs are defined in - * {@link ProtocolAndTechnologyRoute}. - * @param technology Tech-A, Tech-B and Tech-F route destination, where the possible inputs - * are defined in - * {@link ProtocolAndTechnologyRoute} - * @param emptyAid Zero-length AID route destination, where the possible inputs are defined in - * {@link ProtocolAndTechnologyRoute} - * @param systemCode System Code route destination, where the possible inputs are defined in - * {@link ProtocolAndTechnologyRoute} - */ - @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS) - @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) - public void overwriteRoutingTable( - @CardEmulation.ProtocolAndTechnologyRoute int protocol, - @CardEmulation.ProtocolAndTechnologyRoute int technology, - @CardEmulation.ProtocolAndTechnologyRoute int emptyAid, - @CardEmulation.ProtocolAndTechnologyRoute int systemCode) { - - String protocolRoute = routeIntToString(protocol); - String technologyRoute = routeIntToString(technology); - String emptyAidRoute = routeIntToString(emptyAid); - String systemCodeRoute = routeIntToString(systemCode); - - NfcAdapter.callService(() -> - NfcAdapter.sCardEmulationService.overwriteRoutingTable( - mContext.getUser().getIdentifier(), - emptyAidRoute, - protocolRoute, - technologyRoute, - systemCodeRoute - )); - } - - /** - * Gets current routing table entries. - * @return List of {@link NfcRoutingTableEntry} representing current routing table - */ - @NonNull - @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS) - @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) - public List<NfcRoutingTableEntry> getRoutingTable() { - List<Entry> entryList = NfcAdapter.callServiceReturn(() -> - NfcAdapter.sService.getRoutingTableEntryList(), null); - List<NfcRoutingTableEntry> result = new ArrayList<>(); - for (Entry entry : entryList) { - switch (entry.getType()) { - case TYPE_TECHNOLOGY -> result.add( - new RoutingTableTechnologyEntry(entry.getNfceeId(), - RoutingTableTechnologyEntry.techStringToInt(entry.getEntry()), - routeStringToInt(entry.getRoutingType())) - ); - case TYPE_PROTOCOL -> result.add( - new RoutingTableProtocolEntry(entry.getNfceeId(), - RoutingTableProtocolEntry.protocolStringToInt(entry.getEntry()), - routeStringToInt(entry.getRoutingType())) - ); - case TYPE_AID -> result.add( - new RoutingTableAidEntry(entry.getNfceeId(), entry.getEntry(), - routeStringToInt(entry.getRoutingType())) - ); - case TYPE_SYSTEMCODE -> result.add( - new RoutingTableSystemCodeEntry(entry.getNfceeId(), - entry.getEntry().getBytes(StandardCharsets.UTF_8), - routeStringToInt(entry.getRoutingType())) - ); - } - } - return result; - } - - /** - * API to force a routing table commit. - * @return a {@link StatusCode} to indicate if commit routing succeeded or not - */ - @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS) - @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) - @CommitRoutingStatusCode - public int forceRoutingTableCommit() { - return NfcAdapter.callServiceReturn( - () -> NfcAdapter.sService.commitRouting(), COMMIT_ROUTING_STATUS_FAILED); - } - - private final class NfcOemExtensionCallback extends INfcOemExtensionCallback.Stub { - - @Override - public void onTagConnected(boolean connected) throws RemoteException { - mCallbackMap.forEach((cb, ex) -> - handleVoidCallback(connected, cb::onTagConnected, ex)); - } - - @Override - public void onCardEmulationActivated(boolean isActivated) throws RemoteException { - mCardEmulationActivated = isActivated; - mCallbackMap.forEach((cb, ex) -> - handleVoidCallback(isActivated, cb::onCardEmulationActivated, ex)); - } - - @Override - public void onRfFieldDetected(boolean isActive) throws RemoteException { - mRfFieldActivated = isActive; - mCallbackMap.forEach((cb, ex) -> - handleVoidCallback(isActive, cb::onRfFieldDetected, ex)); - } - - @Override - public void onRfDiscoveryStarted(boolean isDiscoveryStarted) throws RemoteException { - mRfDiscoveryStarted = isDiscoveryStarted; - mCallbackMap.forEach((cb, ex) -> - handleVoidCallback(isDiscoveryStarted, cb::onRfDiscoveryStarted, ex)); - } - - @Override - public void onEeListenActivated(boolean isActivated) throws RemoteException { - mEeListenActivated = isActivated; - mCallbackMap.forEach((cb, ex) -> - handleVoidCallback(isActivated, cb::onEeListenActivated, ex)); - } - - @Override - public void onEeUpdated() throws RemoteException { - mCallbackMap.forEach((cb, ex) -> - handleVoidCallback(null, (Object input) -> cb.onEeUpdated(), ex)); - } - - @Override - public void onStateUpdated(int state) throws RemoteException { - mCallbackMap.forEach((cb, ex) -> - handleVoidCallback(state, cb::onStateUpdated, ex)); - } - - @Override - public void onApplyRouting(ResultReceiver isSkipped) throws RemoteException { - mCallbackMap.forEach((cb, ex) -> - handleVoidCallback( - new ReceiverWrapper<>(isSkipped), cb::onApplyRouting, ex)); - } - @Override - public void onNdefRead(ResultReceiver isSkipped) throws RemoteException { - mCallbackMap.forEach((cb, ex) -> - handleVoidCallback( - new ReceiverWrapper<>(isSkipped), cb::onNdefRead, ex)); - } - @Override - public void onEnable(ResultReceiver isAllowed) throws RemoteException { - mCallbackMap.forEach((cb, ex) -> - handleVoidCallback( - new ReceiverWrapper<>(isAllowed), cb::onEnableRequested, ex)); - } - @Override - public void onDisable(ResultReceiver isAllowed) throws RemoteException { - mCallbackMap.forEach((cb, ex) -> - handleVoidCallback( - new ReceiverWrapper<>(isAllowed), cb::onDisableRequested, ex)); - } - @Override - public void onBootStarted() throws RemoteException { - mCallbackMap.forEach((cb, ex) -> - handleVoidCallback(null, (Object input) -> cb.onBootStarted(), ex)); - } - @Override - public void onEnableStarted() throws RemoteException { - mCallbackMap.forEach((cb, ex) -> - handleVoidCallback(null, (Object input) -> cb.onEnableStarted(), ex)); - } - @Override - public void onDisableStarted() throws RemoteException { - mCallbackMap.forEach((cb, ex) -> - handleVoidCallback(null, (Object input) -> cb.onDisableStarted(), ex)); - } - @Override - public void onBootFinished(int status) throws RemoteException { - mCallbackMap.forEach((cb, ex) -> - handleVoidCallback(status, cb::onBootFinished, ex)); - } - @Override - public void onEnableFinished(int status) throws RemoteException { - mCallbackMap.forEach((cb, ex) -> - handleVoidCallback(status, cb::onEnableFinished, ex)); - } - @Override - public void onDisableFinished(int status) throws RemoteException { - mCallbackMap.forEach((cb, ex) -> - handleVoidCallback(status, cb::onDisableFinished, ex)); - } - @Override - public void onTagDispatch(ResultReceiver isSkipped) throws RemoteException { - mCallbackMap.forEach((cb, ex) -> - handleVoidCallback( - new ReceiverWrapper<>(isSkipped), cb::onTagDispatch, ex)); - } - @Override - public void onRoutingChanged(ResultReceiver isSkipped) throws RemoteException { - mCallbackMap.forEach((cb, ex) -> - handleVoidCallback( - new ReceiverWrapper<>(isSkipped), cb::onRoutingChanged, ex)); - } - @Override - public void onHceEventReceived(int action) throws RemoteException { - mCallbackMap.forEach((cb, ex) -> - handleVoidCallback(action, cb::onHceEventReceived, ex)); - } - - @Override - public void onReaderOptionChanged(boolean enabled) throws RemoteException { - mCallbackMap.forEach((cb, ex) -> - handleVoidCallback(enabled, cb::onReaderOptionChanged, ex)); - } - - public void onRoutingTableFull() throws RemoteException { - mCallbackMap.forEach((cb, ex) -> - handleVoidCallback(null, - (Object input) -> cb.onRoutingTableFull(), ex)); - } - - @Override - public void onGetOemAppSearchIntent(List<String> packages, ResultReceiver intentConsumer) - throws RemoteException { - mCallbackMap.forEach((cb, ex) -> - handleVoid2ArgCallback(packages, new ReceiverWrapper<>(intentConsumer), - cb::onGetOemAppSearchIntent, ex)); - } - - @Override - public void onNdefMessage(Tag tag, NdefMessage message, - ResultReceiver hasOemExecutableContent) throws RemoteException { - mCallbackMap.forEach((cb, ex) -> { - synchronized (mLock) { - final long identity = Binder.clearCallingIdentity(); - try { - ex.execute(() -> cb.onNdefMessage( - tag, message, new ReceiverWrapper<>(hasOemExecutableContent))); - } catch (RuntimeException exception) { - throw exception; - } finally { - Binder.restoreCallingIdentity(identity); - } - } - }); - } - - @Override - public void onLaunchHceAppChooserActivity(String selectedAid, - List<ApduServiceInfo> services, - ComponentName failedComponent, String category) - throws RemoteException { - mCallbackMap.forEach((cb, ex) -> { - synchronized (mLock) { - final long identity = Binder.clearCallingIdentity(); - try { - ex.execute(() -> cb.onLaunchHceAppChooserActivity( - selectedAid, services, failedComponent, category)); - } catch (RuntimeException exception) { - throw exception; - } finally { - Binder.restoreCallingIdentity(identity); - } - } - }); - } - - @Override - public void onLaunchHceTapAgainActivity(ApduServiceInfo service, String category) - throws RemoteException { - mCallbackMap.forEach((cb, ex) -> - handleVoid2ArgCallback(service, category, cb::onLaunchHceTapAgainDialog, ex)); - } - - @Override - public void onLogEventNotified(OemLogItems item) throws RemoteException { - mCallbackMap.forEach((cb, ex) -> - handleVoidCallback(item, cb::onLogEventNotified, ex)); - } - - @Override - public void onExtractOemPackages(NdefMessage message, ResultReceiver packageConsumer) - throws RemoteException { - mCallbackMap.forEach((cb, ex) -> - handleVoid2ArgCallback(message, - new ReceiverWrapper<>(packageConsumer), - cb::onExtractOemPackages, ex)); - } - - private <T> void handleVoidCallback( - T input, Consumer<T> callbackMethod, Executor executor) { - synchronized (mLock) { - final long identity = Binder.clearCallingIdentity(); - try { - executor.execute(() -> callbackMethod.accept(input)); - } catch (RuntimeException ex) { - throw ex; - } finally { - Binder.restoreCallingIdentity(identity); - } - } - } - - private <T1, T2> void handleVoid2ArgCallback( - T1 input1, T2 input2, BiConsumer<T1, T2> callbackMethod, Executor executor) { - synchronized (mLock) { - final long identity = Binder.clearCallingIdentity(); - try { - executor.execute(() -> callbackMethod.accept(input1, input2)); - } catch (RuntimeException ex) { - throw ex; - } finally { - Binder.restoreCallingIdentity(identity); - } - } - } - - private <S, T> S handleNonVoidCallbackWithInput( - S defaultValue, T input, Function<T, S> callbackMethod) throws RemoteException { - synchronized (mLock) { - final long identity = Binder.clearCallingIdentity(); - S result = defaultValue; - try { - ExecutorService executor = Executors.newSingleThreadExecutor(); - FutureTask<S> futureTask = new FutureTask<>(() -> callbackMethod.apply(input)); - var unused = executor.submit(futureTask); - try { - result = futureTask.get( - OEM_EXTENSION_RESPONSE_THRESHOLD_MS, TimeUnit.MILLISECONDS); - } catch (ExecutionException | InterruptedException e) { - e.printStackTrace(); - } catch (TimeoutException e) { - Log.w(TAG, "Callback timed out: " + callbackMethod); - e.printStackTrace(); - } finally { - executor.shutdown(); - } - } finally { - Binder.restoreCallingIdentity(identity); - } - return result; - } - } - - private <T> T handleNonVoidCallbackWithoutInput(T defaultValue, Supplier<T> callbackMethod) - throws RemoteException { - synchronized (mLock) { - final long identity = Binder.clearCallingIdentity(); - T result = defaultValue; - try { - ExecutorService executor = Executors.newSingleThreadExecutor(); - FutureTask<T> futureTask = new FutureTask<>(callbackMethod::get); - var unused = executor.submit(futureTask); - try { - result = futureTask.get( - OEM_EXTENSION_RESPONSE_THRESHOLD_MS, TimeUnit.MILLISECONDS); - } catch (ExecutionException | InterruptedException e) { - e.printStackTrace(); - } catch (TimeoutException e) { - Log.w(TAG, "Callback timed out: " + callbackMethod); - e.printStackTrace(); - } finally { - executor.shutdown(); - } - } finally { - Binder.restoreCallingIdentity(identity); - } - return result; - } - } - } - - private @CardEmulation.ProtocolAndTechnologyRoute int routeStringToInt(String route) { - if (route.equals("DH")) { - return PROTOCOL_AND_TECHNOLOGY_ROUTE_DH; - } else if (route.startsWith("eSE")) { - return PROTOCOL_AND_TECHNOLOGY_ROUTE_ESE; - } else if (route.startsWith("SIM")) { - return PROTOCOL_AND_TECHNOLOGY_ROUTE_UICC; - } else { - throw new IllegalStateException("Unexpected value: " + route); - } - } - - private class ReceiverWrapper<T> implements Consumer<T> { - private final ResultReceiver mResultReceiver; - - ReceiverWrapper(ResultReceiver resultReceiver) { - mResultReceiver = resultReceiver; - } - - @Override - public void accept(T result) { - if (result instanceof Boolean) { - mResultReceiver.send((Boolean) result ? 1 : 0, null); - } else if (result instanceof Intent) { - Bundle bundle = new Bundle(); - bundle.putParcelable("intent", (Intent) result); - mResultReceiver.send(0, bundle); - } else if (result instanceof List<?> list) { - if (list.stream().allMatch(String.class::isInstance)) { - Bundle bundle = new Bundle(); - bundle.putStringArray("packageNames", - list.stream().map(pkg -> (String) pkg).toArray(String[]::new)); - mResultReceiver.send(0, bundle); - } - } - } - - @Override - public Consumer<T> andThen(Consumer<? super T> after) { - return Consumer.super.andThen(after); - } - } -} diff --git a/nfc/java/android/nfc/NfcRoutingTableEntry.java b/nfc/java/android/nfc/NfcRoutingTableEntry.java deleted file mode 100644 index 4153779a8ba2..000000000000 --- a/nfc/java/android/nfc/NfcRoutingTableEntry.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (C) 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.SystemApi; -import android.nfc.cardemulation.CardEmulation; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Class to represent an entry of routing table. This class is abstract and extended by - * {@link RoutingTableTechnologyEntry}, {@link RoutingTableProtocolEntry}, - * {@link RoutingTableAidEntry} and {@link RoutingTableSystemCodeEntry}. - * - * @hide - */ -@FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) -@SystemApi -public abstract class NfcRoutingTableEntry { - private final int mNfceeId; - private final int mType; - private final int mRouteType; - - /** - * AID routing table type. - */ - public static final int TYPE_AID = 0; - /** - * Protocol routing table type. - */ - public static final int TYPE_PROTOCOL = 1; - /** - * Technology routing table type. - */ - public static final int TYPE_TECHNOLOGY = 2; - /** - * System Code routing table type. - */ - public static final int TYPE_SYSTEM_CODE = 3; - - /** - * Possible type of this routing table entry. - * @hide - */ - @IntDef(prefix = "TYPE_", value = { - TYPE_AID, - TYPE_PROTOCOL, - TYPE_TECHNOLOGY, - TYPE_SYSTEM_CODE - }) - @Retention(RetentionPolicy.SOURCE) - public @interface RoutingTableType {} - - /** @hide */ - protected NfcRoutingTableEntry(int nfceeId, @RoutingTableType int type, - @CardEmulation.ProtocolAndTechnologyRoute int routeType) { - mNfceeId = nfceeId; - mType = type; - mRouteType = routeType; - } - - /** - * Gets the NFCEE Id of this entry. - * @return an integer of NFCEE Id. - */ - public int getNfceeId() { - return mNfceeId; - } - - /** - * Get the type of this entry. - * @return an integer defined in {@link RoutingTableType} - */ - @RoutingTableType - public int getType() { - return mType; - } - - /** - * Get the route type of this entry. - * @return an integer defined in - * {@link android.nfc.cardemulation.CardEmulation.ProtocolAndTechnologyRoute} - */ - @CardEmulation.ProtocolAndTechnologyRoute - public int getRouteType() { - return mRouteType; - } -} diff --git a/nfc/java/android/nfc/NfcVendorNciCallbackListener.java b/nfc/java/android/nfc/NfcVendorNciCallbackListener.java deleted file mode 100644 index 742d75fe4bc3..000000000000 --- a/nfc/java/android/nfc/NfcVendorNciCallbackListener.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (C) 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.NonNull; -import android.nfc.NfcAdapter.NfcVendorNciCallback; -import android.os.Binder; -import android.os.RemoteException; -import android.util.Log; - -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.Executor; - -/** - * @hide - */ -public final class NfcVendorNciCallbackListener extends INfcVendorNciCallback.Stub { - private static final String TAG = "Nfc.NfcVendorNciCallbacks"; - private final INfcAdapter mAdapter; - private boolean mIsRegistered = false; - private final Map<NfcVendorNciCallback, Executor> mCallbackMap = new HashMap<>(); - - public NfcVendorNciCallbackListener(@NonNull INfcAdapter adapter) { - mAdapter = adapter; - } - - public void register(@NonNull Executor executor, @NonNull NfcVendorNciCallback callback) { - synchronized (this) { - if (mCallbackMap.containsKey(callback)) { - return; - } - mCallbackMap.put(callback, executor); - if (!mIsRegistered) { - try { - mAdapter.registerVendorExtensionCallback(this); - mIsRegistered = true; - } catch (RemoteException e) { - Log.w(TAG, "Failed to register adapter state callback"); - mCallbackMap.remove(callback); - throw e.rethrowFromSystemServer(); - } - } - } - } - - public void unregister(@NonNull NfcVendorNciCallback callback) { - synchronized (this) { - if (!mCallbackMap.containsKey(callback) || !mIsRegistered) { - return; - } - if (mCallbackMap.size() == 1) { - try { - mAdapter.unregisterVendorExtensionCallback(this); - mIsRegistered = false; - mCallbackMap.remove(callback); - } catch (RemoteException e) { - Log.w(TAG, "Failed to unregister AdapterStateCallback with service"); - throw e.rethrowFromSystemServer(); - } - } else { - mCallbackMap.remove(callback); - } - } - } - - @Override - public void onVendorResponseReceived(int gid, int oid, @NonNull byte[] payload) - throws RemoteException { - synchronized (this) { - final long identity = Binder.clearCallingIdentity(); - try { - for (NfcVendorNciCallback callback : mCallbackMap.keySet()) { - Executor executor = mCallbackMap.get(callback); - executor.execute(() -> callback.onVendorNciResponse(gid, oid, payload)); - } - } catch (RuntimeException ex) { - throw ex; - } finally { - Binder.restoreCallingIdentity(identity); - } - } - } - - @Override - public void onVendorNotificationReceived(int gid, int oid, @NonNull byte[] payload) - throws RemoteException { - synchronized (this) { - final long identity = Binder.clearCallingIdentity(); - try { - for (NfcVendorNciCallback callback : mCallbackMap.keySet()) { - Executor executor = mCallbackMap.get(callback); - executor.execute(() -> callback.onVendorNciNotification(gid, oid, payload)); - } - } catch (RuntimeException ex) { - throw ex; - } finally { - Binder.restoreCallingIdentity(identity); - } - } - } -} diff --git a/nfc/java/android/nfc/NfcWlcStateListener.java b/nfc/java/android/nfc/NfcWlcStateListener.java deleted file mode 100644 index 890cb090f587..000000000000 --- a/nfc/java/android/nfc/NfcWlcStateListener.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright 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; - -import android.annotation.NonNull; -import android.nfc.NfcAdapter.WlcStateListener; -import android.os.Binder; -import android.os.RemoteException; -import android.util.Log; - -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.Executor; - -/** - * @hide - */ -public class NfcWlcStateListener extends INfcWlcStateListener.Stub { - private static final String TAG = NfcWlcStateListener.class.getSimpleName(); - - private final INfcAdapter mAdapter; - - private final Map<WlcStateListener, Executor> mListenerMap = new HashMap<>(); - - private WlcListenerDeviceInfo mCurrentState = null; - private boolean mIsRegistered = false; - - public NfcWlcStateListener(@NonNull INfcAdapter adapter) { - mAdapter = adapter; - } - - /** - * Register a {@link WlcStateListener} with this - * {@link WlcStateListener} - * - * @param executor an {@link Executor} to execute given listener - * @param listener user implementation of the {@link WlcStateListener} - */ - public void register(@NonNull Executor executor, @NonNull WlcStateListener listener) { - synchronized (this) { - if (mListenerMap.containsKey(listener)) { - return; - } - - mListenerMap.put(listener, executor); - - if (!mIsRegistered) { - try { - mAdapter.registerWlcStateListener(this); - mIsRegistered = true; - } catch (RemoteException e) { - Log.w(TAG, "Failed to register"); - } - } - } - } - - /** - * Unregister the specified {@link WlcStateListener} - * - * @param listener user implementation of the {@link WlcStateListener} - */ - public void unregister(@NonNull WlcStateListener listener) { - synchronized (this) { - if (!mListenerMap.containsKey(listener)) { - return; - } - - mListenerMap.remove(listener); - - if (mListenerMap.isEmpty() && mIsRegistered) { - try { - mAdapter.unregisterWlcStateListener(this); - } catch (RemoteException e) { - Log.w(TAG, "Failed to unregister"); - } - mIsRegistered = false; - } - } - } - - private void sendCurrentState(@NonNull WlcStateListener listener) { - synchronized (this) { - Executor executor = mListenerMap.get(listener); - final long identity = Binder.clearCallingIdentity(); - try { - if (Flags.enableNfcCharging()) { - executor.execute(() -> listener.onWlcStateChanged( - mCurrentState)); - } - } finally { - Binder.restoreCallingIdentity(identity); - } - } - } - - @Override - public void onWlcStateChanged(@NonNull WlcListenerDeviceInfo wlcListenerDeviceInfo) { - synchronized (this) { - mCurrentState = wlcListenerDeviceInfo; - - for (WlcStateListener cb : mListenerMap.keySet()) { - sendCurrentState(cb); - } - } - } -} - diff --git a/nfc/java/android/nfc/OemLogItems.aidl b/nfc/java/android/nfc/OemLogItems.aidl deleted file mode 100644 index 3bcb445fc7d2..000000000000 --- a/nfc/java/android/nfc/OemLogItems.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 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; - -parcelable OemLogItems;
\ No newline at end of file diff --git a/nfc/java/android/nfc/OemLogItems.java b/nfc/java/android/nfc/OemLogItems.java deleted file mode 100644 index 4f3e1999f5d3..000000000000 --- a/nfc/java/android/nfc/OemLogItems.java +++ /dev/null @@ -1,333 +0,0 @@ -/*
- * 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];
- }
- };
-
-}
diff --git a/nfc/java/android/nfc/Placeholder.java b/nfc/java/android/nfc/Placeholder.java deleted file mode 100644 index 3509644ac106..000000000000 --- a/nfc/java/android/nfc/Placeholder.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * 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; - -/** - * Placeholder class so new framework-nfc module isn't empty, will be removed once module is - * populated. - * - * @hide - * - */ -public class Placeholder { -} diff --git a/nfc/java/android/nfc/RoutingStatus.java b/nfc/java/android/nfc/RoutingStatus.java deleted file mode 100644 index 4a1b1f3cecbc..000000000000 --- a/nfc/java/android/nfc/RoutingStatus.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (C) 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.RequiresPermission; -import android.annotation.SystemApi; -import android.nfc.cardemulation.CardEmulation; - -/** - * A class indicating default route, ISO-DEP route and off-host route. - * - * @hide - */ -@SystemApi -@FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) -public class RoutingStatus { - private final @CardEmulation.ProtocolAndTechnologyRoute int mDefaultRoute; - private final @CardEmulation.ProtocolAndTechnologyRoute int mDefaultIsoDepRoute; - private final @CardEmulation.ProtocolAndTechnologyRoute int mDefaultOffHostRoute; - - RoutingStatus(@CardEmulation.ProtocolAndTechnologyRoute int mDefaultRoute, - @CardEmulation.ProtocolAndTechnologyRoute int mDefaultIsoDepRoute, - @CardEmulation.ProtocolAndTechnologyRoute int mDefaultOffHostRoute) { - this.mDefaultRoute = mDefaultRoute; - this.mDefaultIsoDepRoute = mDefaultIsoDepRoute; - this.mDefaultOffHostRoute = mDefaultOffHostRoute; - } - - /** - * Getter of the default route. - * @return an integer defined in - * {@link android.nfc.cardemulation.CardEmulation.ProtocolAndTechnologyRoute} - */ - @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) - @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - @CardEmulation.ProtocolAndTechnologyRoute - public int getDefaultRoute() { - return mDefaultRoute; - } - - /** - * Getter of the default ISO-DEP route. - * @return an integer defined in - * {@link android.nfc.cardemulation.CardEmulation.ProtocolAndTechnologyRoute} - */ - @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) - @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - @CardEmulation.ProtocolAndTechnologyRoute - public int getDefaultIsoDepRoute() { - return mDefaultIsoDepRoute; - } - - /** - * Getter of the default off-host route. - * @return an integer defined in - * {@link android.nfc.cardemulation.CardEmulation.ProtocolAndTechnologyRoute} - */ - @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) - @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - @CardEmulation.ProtocolAndTechnologyRoute - public int getDefaultOffHostRoute() { - return mDefaultOffHostRoute; - } - -} diff --git a/nfc/java/android/nfc/RoutingTableAidEntry.java b/nfc/java/android/nfc/RoutingTableAidEntry.java deleted file mode 100644 index be94f9fc117c..000000000000 --- a/nfc/java/android/nfc/RoutingTableAidEntry.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 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.NonNull; -import android.annotation.SystemApi; -import android.nfc.cardemulation.CardEmulation; - -/** - * Represents an Application ID (AID) entry in current routing table. - * @hide - */ -@FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) -@SystemApi -public class RoutingTableAidEntry extends NfcRoutingTableEntry { - private final String mValue; - - /** @hide */ - public RoutingTableAidEntry(int nfceeId, String value, - @CardEmulation.ProtocolAndTechnologyRoute int routeType) { - super(nfceeId, TYPE_AID, routeType); - this.mValue = value; - } - - /** - * Gets AID value. - * @return String of AID - */ - @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) - @NonNull - public String getAid() { - return mValue; - } -} diff --git a/nfc/java/android/nfc/RoutingTableProtocolEntry.java b/nfc/java/android/nfc/RoutingTableProtocolEntry.java deleted file mode 100644 index a68d8c167865..000000000000 --- a/nfc/java/android/nfc/RoutingTableProtocolEntry.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (C) 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.SystemApi; -import android.nfc.cardemulation.CardEmulation; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Represents a protocol entry in current routing table. - * @hide - */ -@FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) -@SystemApi -public class RoutingTableProtocolEntry extends NfcRoutingTableEntry { - /** - * Protocol undetermined. - */ - @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) - public static final int PROTOCOL_UNDETERMINED = 0; - /** - * T1T Protocol - */ - @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) - public static final int PROTOCOL_T1T = 1; - /** - * T2T Protocol - */ - @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) - public static final int PROTOCOL_T2T = 2; - /** - * T3T Protocol - */ - @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) - public static final int PROTOCOL_T3T = 3; - /** - * ISO-DEP Protocol - */ - @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) - public static final int PROTOCOL_ISO_DEP = 4; - /** - * DEP Protocol - */ - @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) - public static final int PROTOCOL_NFC_DEP = 5; - /** - * T5T Protocol - */ - @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) - public static final int PROTOCOL_T5T = 6; - /** - * NDEF Protocol - */ - @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) - public static final int PROTOCOL_NDEF = 7; - /** - * Unsupported Protocol - */ - @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) - public static final int PROTOCOL_UNSUPPORTED = -1; - - /** - * - * @hide - */ - @IntDef(prefix = { "PROTOCOL_" }, value = { - PROTOCOL_UNDETERMINED, - PROTOCOL_T1T, - PROTOCOL_T2T, - PROTOCOL_T3T, - PROTOCOL_ISO_DEP, - PROTOCOL_NFC_DEP, - PROTOCOL_T5T, - PROTOCOL_NDEF, - PROTOCOL_UNSUPPORTED - }) - @Retention(RetentionPolicy.SOURCE) - public @interface ProtocolValue {} - - private final @ProtocolValue int mValue; - - /** @hide */ - public RoutingTableProtocolEntry(int nfceeId, @ProtocolValue int value, - @CardEmulation.ProtocolAndTechnologyRoute int routeType) { - super(nfceeId, TYPE_PROTOCOL, routeType); - this.mValue = value; - } - - /** - * Gets Protocol value. - * @return Protocol defined in {@link ProtocolValue} - */ - @ProtocolValue - @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) - public int getProtocol() { - return mValue; - } - - /** @hide */ - @ProtocolValue - public static int protocolStringToInt(String protocolString) { - return switch (protocolString) { - case "PROTOCOL_T1T" -> PROTOCOL_T1T; - case "PROTOCOL_T2T" -> PROTOCOL_T2T; - case "PROTOCOL_T3T" -> PROTOCOL_T3T; - case "PROTOCOL_ISO_DEP" -> PROTOCOL_ISO_DEP; - case "PROTOCOL_NFC_DEP" -> PROTOCOL_NFC_DEP; - case "PROTOCOL_T5T" -> PROTOCOL_T5T; - case "PROTOCOL_NDEF" -> PROTOCOL_NDEF; - case "PROTOCOL_UNDETERMINED" -> PROTOCOL_UNDETERMINED; - default -> PROTOCOL_UNSUPPORTED; - }; - } -} diff --git a/nfc/java/android/nfc/RoutingTableSystemCodeEntry.java b/nfc/java/android/nfc/RoutingTableSystemCodeEntry.java deleted file mode 100644 index 06cc0a5f26f1..000000000000 --- a/nfc/java/android/nfc/RoutingTableSystemCodeEntry.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 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.NonNull; -import android.annotation.SystemApi; -import android.nfc.cardemulation.CardEmulation; - -/** - * Represents a system code entry in current routing table, where system codes are two-byte values - * used in NFC-F technology (a type of NFC communication) to identify specific - * device configurations. - * @hide - */ -@FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) -@SystemApi -public class RoutingTableSystemCodeEntry extends NfcRoutingTableEntry { - private final byte[] mValue; - - /** @hide */ - public RoutingTableSystemCodeEntry(int nfceeId, byte[] value, - @CardEmulation.ProtocolAndTechnologyRoute int routeType) { - super(nfceeId, TYPE_SYSTEM_CODE, routeType); - this.mValue = value; - } - - /** - * Gets system code value. - * @return Byte array of system code - */ - @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) - @NonNull - public byte[] getSystemCode() { - return mValue; - } -} diff --git a/nfc/java/android/nfc/RoutingTableTechnologyEntry.java b/nfc/java/android/nfc/RoutingTableTechnologyEntry.java deleted file mode 100644 index 86239ce7a6b2..000000000000 --- a/nfc/java/android/nfc/RoutingTableTechnologyEntry.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (C) 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.SystemApi; -import android.nfc.cardemulation.CardEmulation; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Represents a technology entry in current routing table. - * @hide - */ -@FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) -@SystemApi -public class RoutingTableTechnologyEntry extends NfcRoutingTableEntry { - /** - * Technology-A. - * <p>Tech-A is mostly used for payment and ticketing applications. It supports various - * Tag platforms including Type 1, Type 2 and Type 4A tags. </p> - */ - @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) - public static final int TECHNOLOGY_A = 0; - /** - * Technology-B which is based on ISO/IEC 14443-3 standard. - */ - @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) - public static final int TECHNOLOGY_B = 1; - /** - * Technology-F. - * <p>Tech-F is a standard which supports Type 3 Tags and NFC-DEP protocol etc.</p> - */ - @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) - public static final int TECHNOLOGY_F = 2; - /** - * Technology-V. - * <p>Tech-V is an NFC technology used for communication with passive tags that operate - * at a longer range than other NFC technologies. </p> - */ - @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) - public static final int TECHNOLOGY_V = 3; - /** - * Unsupported technology - */ - @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) - public static final int TECHNOLOGY_UNSUPPORTED = -1; - - /** - * - * @hide - */ - @IntDef(prefix = { "TECHNOLOGY_" }, value = { - TECHNOLOGY_A, - TECHNOLOGY_B, - TECHNOLOGY_F, - TECHNOLOGY_V, - TECHNOLOGY_UNSUPPORTED - }) - @Retention(RetentionPolicy.SOURCE) - public @interface TechnologyValue{} - - private final @TechnologyValue int mValue; - - /** @hide */ - public RoutingTableTechnologyEntry(int nfceeId, @TechnologyValue int value, - @CardEmulation.ProtocolAndTechnologyRoute int routeType) { - super(nfceeId, TYPE_TECHNOLOGY, routeType); - this.mValue = value; - } - - /** - * Gets technology value. - * @return technology value - */ - @TechnologyValue - @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) - public int getTechnology() { - return mValue; - } - - /** @hide */ - @TechnologyValue - public static int techStringToInt(String tech) { - return switch (tech) { - case "TECHNOLOGY_A" -> TECHNOLOGY_A; - case "TECHNOLOGY_B" -> TECHNOLOGY_B; - case "TECHNOLOGY_F" -> TECHNOLOGY_F; - case "TECHNOLOGY_V" -> TECHNOLOGY_V; - default -> TECHNOLOGY_UNSUPPORTED; - }; - } -} diff --git a/nfc/java/android/nfc/T4tNdefNfcee.java b/nfc/java/android/nfc/T4tNdefNfcee.java deleted file mode 100644 index 05a30aad76fc..000000000000 --- a/nfc/java/android/nfc/T4tNdefNfcee.java +++ /dev/null @@ -1,276 +0,0 @@ -/* - * Copyright (C) 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.IntRange; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.RequiresPermission; -import android.annotation.SystemApi; -import android.annotation.WorkerThread; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * This class is used for performing T4T (Type-4 Tag) NDEF (NFC Data Exchange Format) - * NFCEE (NFC Execution Environment) operations. - * This can be used to write NDEF data to emulate a T4T tag in an NFCEE - * (NFC Execution Environment - eSE, SIM, etc). Refer to the NFC forum specification - * "NFCForum-TS-NCI-2.3 section 10.4" and "NFCForum-TS-T4T-1.1 section 4.2" for more details. - * @hide - */ -@FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) -@SystemApi -public final class T4tNdefNfcee { - private static final String TAG = "NdefNfcee"; - static T4tNdefNfcee sNdefNfcee; - - private T4tNdefNfcee() { - } - - /** - * Helper to get an instance of this class. - * - * @return - * @hide - */ - @NonNull - public static T4tNdefNfcee getInstance() { - if (sNdefNfcee == null) { - sNdefNfcee = new T4tNdefNfcee(); - } - return sNdefNfcee; - } - - /** - * Return flag for {@link #writeData(int, byte[])}. - * It indicates write data is successful. - */ - public static final int WRITE_DATA_SUCCESS = 0; - /** - * Return flag for {@link #writeData(int, byte[])}. - * It indicates write data fail due to unknown reasons. - */ - public static final int WRITE_DATA_ERROR_INTERNAL = -1; - /** - * Return flag for {@link #writeData(int, byte[])}. - * It indicates write data fail due to ongoing rf activity. - */ - public static final int WRITE_DATA_ERROR_RF_ACTIVATED = -2; - /** - * Return flag for {@link #writeData(int, byte[])}. - * It indicates write data fail due to Nfc off. - */ - public static final int WRITE_DATA_ERROR_NFC_NOT_ON = -3; - /** - * Return flag for {@link #writeData(int, byte[])}. - * It indicates write data fail due to invalid file id. - */ - public static final int WRITE_DATA_ERROR_INVALID_FILE_ID = -4; - /** - * Return flag for {@link #writeData(int, byte[])}. - * It indicates write data fail due to invalid length. - */ - public static final int WRITE_DATA_ERROR_INVALID_LENGTH = -5; - /** - * Return flag for {@link #writeData(int, byte[])}. - * It indicates write data fail due to core connection create failure. - */ - public static final int WRITE_DATA_ERROR_CONNECTION_FAILED = -6; - /** - * Return flag for {@link #writeData(int, byte[])}. - * It indicates write data fail due to empty payload. - */ - public static final int WRITE_DATA_ERROR_EMPTY_PAYLOAD = -7; - /** - * Returns flag for {@link #writeData(int, byte[])}. - * It indicates write data fail due to invalid ndef format. - */ - public static final int WRITE_DATA_ERROR_NDEF_VALIDATION_FAILED = -8; - /** - * Returns flag for {@link #writeData(int, byte[])}. - * It indicates write data fail if a concurrent NDEF NFCEE operation is ongoing. - */ - public static final int WRITE_DATA_ERROR_DEVICE_BUSY = -9; - - /** - * Possible return values for {@link #writeData(int, byte[])}. - * - * @hide - */ - @IntDef(prefix = { "WRITE_DATA_" }, value = { - WRITE_DATA_SUCCESS, - WRITE_DATA_ERROR_INTERNAL, - WRITE_DATA_ERROR_RF_ACTIVATED, - WRITE_DATA_ERROR_NFC_NOT_ON, - WRITE_DATA_ERROR_INVALID_FILE_ID, - WRITE_DATA_ERROR_INVALID_LENGTH, - WRITE_DATA_ERROR_CONNECTION_FAILED, - WRITE_DATA_ERROR_EMPTY_PAYLOAD, - WRITE_DATA_ERROR_NDEF_VALIDATION_FAILED, - WRITE_DATA_ERROR_DEVICE_BUSY, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface WriteDataStatus{} - - /** - * This API performs writes of T4T data to NFCEE. - * - * <p>This is an I/O operation and will block until complete. It must - * not be called from the main application thread.</p> - * <p>Applications must send complete Ndef Message payload, do not need to fragment - * the payload, it will be automatically fragmented and defragmented by - * {@link #writeData} if it exceeds max message length limits</p> - * - * @param fileId File id (Refer NFC Forum Type 4 Tag Specification - * Section 4.2 File Identifiers and Access Conditions - * for more information) to which to write. - * @param data This should be valid Ndef Message format. - * Refer to Nfc forum NDEF specification NDEF Message section - * @return status of the operation. - * @hide - */ - @SystemApi - @WorkerThread - @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - public @WriteDataStatus int writeData(@IntRange(from = 0, to = 65535) int fileId, - @NonNull byte[] data) { - return NfcAdapter.callServiceReturn(() -> - NfcAdapter.sNdefNfceeService.writeData(fileId, data), WRITE_DATA_ERROR_INTERNAL); - } - - /** - * This API performs reading of T4T content of Nfcee. - * - * <p>This is an I/O operation and will block until complete. It must - * not be called from the main application thread.</p> - * - * @param fileId File Id (Refer - * Section 4.2 File Identifiers and Access Conditions - * for more information) from which to read. - * @return - Returns complete Ndef message if success - * Refer to Nfc forum NDEF specification NDEF Message section - * @throws IllegalStateException if read fails because the fileId is invalid - * or if a concurrent operation is in progress. - * @hide - */ - @SystemApi - @WorkerThread - @NonNull - @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - public byte[] readData(@IntRange(from = 0, to = 65535) int fileId) { - return NfcAdapter.callServiceReturn(() -> - NfcAdapter.sNdefNfceeService.readData(fileId), null); - } - - /** - * Return flag for {@link #clearNdefData()}. - * It indicates clear data is successful. - */ - public static final int CLEAR_DATA_SUCCESS = 1; - /** - * Return flag for {@link #clearNdefData()}. - * It indicates clear data failed due to internal error while processing the clear. - */ - public static final int CLEAR_DATA_FAILED_INTERNAL = 0; - /** - * Return flag for {@link #clearNdefData()}. - * It indicates clear data failed if a concurrent NDEF NFCEE operation is ongoing. - */ - public static final int CLEAR_DATA_FAILED_DEVICE_BUSY = -1; - - - /** - * Possible return values for {@link #clearNdefData()}. - * - * @hide - */ - @IntDef(prefix = { "CLEAR_DATA_" }, value = { - CLEAR_DATA_SUCCESS, - CLEAR_DATA_FAILED_INTERNAL, - CLEAR_DATA_FAILED_DEVICE_BUSY, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface ClearDataStatus{} - - /** - * This API will set all the T4T NDEF NFCEE data to zero. - * - * <p>This is an I/O operation and will block until complete. It must - * not be called from the main application thread. - * - * <p>This API can be called regardless of NDEF file lock state. - * </p> - * @return status of the operation - * - * @hide - */ - @SystemApi - @WorkerThread - @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - public @ClearDataStatus int clearData() { - return NfcAdapter.callServiceReturn(() -> - NfcAdapter.sNdefNfceeService.clearNdefData(), CLEAR_DATA_FAILED_INTERNAL); - } - - /** - * Returns whether NDEF NFCEE operation is ongoing or not. - * - * @return true if NDEF NFCEE operation is ongoing, else false. - * @hide - */ - @SystemApi - @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - public boolean isOperationOngoing() { - return NfcAdapter.callServiceReturn(() -> - NfcAdapter.sNdefNfceeService.isNdefOperationOngoing(), false); - } - - /** - * This Api is to check the status of NDEF NFCEE emulation feature is - * supported or not. - * - * @return true if NDEF NFCEE emulation feature is supported, else false. - * @hide - */ - @SystemApi - @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - public boolean isSupported() { - return NfcAdapter.callServiceReturn(() -> - NfcAdapter.sNdefNfceeService.isNdefNfceeEmulationSupported(), false); - } - - /** - * This API performs reading of T4T NDEF NFCEE CC file content. - * - * Refer to the NFC forum specification "NFCForum-TS-T4T-1.1 section 4.4" for more details. - * - * @return Returns CC file content if success or null if failed to read. - * @throws IllegalStateException if the device is busy. - * @hide - */ - @SystemApi - @WorkerThread - @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - @Nullable - public T4tNdefNfceeCcFileInfo readCcfile() { - return NfcAdapter.callServiceReturn(() -> - NfcAdapter.sNdefNfceeService.readCcfile(), null); - } -} diff --git a/nfc/java/android/nfc/T4tNdefNfceeCcFileInfo.java b/nfc/java/android/nfc/T4tNdefNfceeCcFileInfo.java deleted file mode 100644 index ce67f8f9aea7..000000000000 --- a/nfc/java/android/nfc/T4tNdefNfceeCcFileInfo.java +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright (C) 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.IntRange; -import android.annotation.NonNull; -import android.annotation.SystemApi; -import android.os.Parcel; -import android.os.Parcelable; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * This class is used to represence T4T (Type-4 Tag) NDEF (NFC Data Exchange Format) - * NFCEE (NFC Execution Environment) CC (Capability Container) File data. - * The CC file stores metadata about the T4T tag being emulated. - * - * Refer to the NFC forum specification "NFCForum-TS-T4T-1.1 section 4.4" for more details. - * @hide - */ -@FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) -@SystemApi -public final class T4tNdefNfceeCcFileInfo implements Parcelable { - /** - * Indicates the size of this capability container (called “CC File”)<p> - */ - private int mCcLength; - /** - * Indicates the mapping specification version<p> - */ - private int mVersion; - /** - * Indicates the NDEF File Identifier<p> - */ - private int mFileId; - /** - * Indicates the maximum Max NDEF file size<p> - */ - private int mMaxSize; - /** - * Indicates the read access condition<p> - */ - private boolean mIsReadAllowed; - /** - * Indicates the write access condition<p> - */ - private boolean mIsWriteAllowed; - - /** - * Constructor to be used by NFC service and internal classes. - * @hide - */ - public T4tNdefNfceeCcFileInfo(int cclen, int version, - int ndefFileId, int ndefMaxSize, - boolean isReadAllowed, boolean isWriteAllowed) { - mCcLength = cclen; - mVersion = version; - mFileId = ndefFileId; - mMaxSize = ndefMaxSize; - mIsReadAllowed = isReadAllowed; - mIsWriteAllowed = isWriteAllowed; - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeInt(mCcLength); - dest.writeInt(mVersion); - dest.writeInt(mFileId); - dest.writeInt(mMaxSize); - dest.writeBoolean(mIsReadAllowed); - dest.writeBoolean(mIsWriteAllowed); - } - - /** - * Indicates the size of this capability container (called “CC File”). - * - * @return length of the CC file. - */ - @IntRange(from = 0xf, to = 0x7fff) - public int getCcFileLength() { - return mCcLength; - } - - /** - * T4T tag mapping version 2.0. - * Refer to the NFC forum specification "NFCForum-TS-T4T-1.1 section 4.4" for more details. - */ - public static final int VERSION_2_0 = 0x20; - /** - * T4T tag mapping version 2.0. - * Refer to the NFC forum specification "NFCForum-TS-T4T-1.1 section 4.4" for more details. - */ - public static final int VERSION_3_0 = 0x30; - - /** - * Possible return values for {@link #getVersion()}. - * @hide - */ - @IntDef(prefix = { "VERSION_" }, value = { - VERSION_2_0, - VERSION_3_0, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface Version{} - - /** - * Indicates the mapping version of the T4T tag supported. - * - * Refer to the NFC forum specification "NFCForum-TS-T4T-1.1 section 4.5" for more details. - * - * @return version of the specification - */ - @Version - public int getVersion() { - return mVersion; - } - - /** - * Indicates the NDEF File Identifier. This is the identifier used in the last invocation of - * {@link T4tNdefNfcee#writeData(int, byte[])} - * - * @return FileId of the data stored or -1 if no data is present. - */ - @IntRange(from = -1, to = 65535) - public int getFileId() { - return mFileId; - } - - /** - * Indicates the maximum size of T4T NDEF data that can be written to the NFCEE. - * - * @return max size of the contents. - */ - @IntRange(from = 0x5, to = 0x7fff) - public int getMaxSize() { - return mMaxSize; - } - - /** - * Indicates the read access condition. - * Refer to the NFC forum specification "NFCForum-TS-T4T-1.1 section 4.2" for more details. - * @return boolean true if read access is allowed, otherwise false. - */ - public boolean isReadAllowed() { - return mIsReadAllowed; - } - - /** - * Indicates the write access condition. - * Refer to the NFC forum specification "NFCForum-TS-T4T-1.1 section 4.2" for more details. - * @return boolean if write access is allowed, otherwise false. - */ - public boolean isWriteAllowed() { - return mIsWriteAllowed; - } - - @Override - public int describeContents() { - return 0; - } - - public static final @NonNull Parcelable.Creator<T4tNdefNfceeCcFileInfo> CREATOR = - new Parcelable.Creator<>() { - @Override - public T4tNdefNfceeCcFileInfo createFromParcel(Parcel in) { - - // NdefNfceeCcFileInfo fields - int cclen = in.readInt(); - int version = in.readInt(); - int ndefFileId = in.readInt(); - int ndefMaxSize = in.readInt(); - boolean isReadAllowed = in.readBoolean(); - boolean isWriteAllowed = in.readBoolean(); - - return new T4tNdefNfceeCcFileInfo(cclen, version, - ndefFileId, ndefMaxSize, - isReadAllowed, isWriteAllowed); - } - - @Override - public T4tNdefNfceeCcFileInfo[] newArray(int size) { - return new T4tNdefNfceeCcFileInfo[size]; - } - }; -} diff --git a/nfc/java/android/nfc/Tag.aidl b/nfc/java/android/nfc/Tag.aidl deleted file mode 100644 index 312261ebe76d..000000000000 --- a/nfc/java/android/nfc/Tag.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2010 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; - -parcelable Tag;
\ No newline at end of file diff --git a/nfc/java/android/nfc/Tag.java b/nfc/java/android/nfc/Tag.java deleted file mode 100644 index 500038f14d9d..000000000000 --- a/nfc/java/android/nfc/Tag.java +++ /dev/null @@ -1,508 +0,0 @@ -/* - * Copyright (C) 2010 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.compat.annotation.UnsupportedAppUsage; -import android.content.Context; -import android.nfc.tech.IsoDep; -import android.nfc.tech.MifareClassic; -import android.nfc.tech.MifareUltralight; -import android.nfc.tech.Ndef; -import android.nfc.tech.NdefFormatable; -import android.nfc.tech.NfcA; -import android.nfc.tech.NfcB; -import android.nfc.tech.NfcBarcode; -import android.nfc.tech.NfcF; -import android.nfc.tech.NfcV; -import android.nfc.tech.TagTechnology; -import android.os.Build; -import android.os.Bundle; -import android.os.Parcel; -import android.os.Parcelable; -import android.os.RemoteException; - -import java.io.IOException; -import java.util.Arrays; -import java.util.HashMap; - -/** - * Represents an NFC tag that has been discovered. - * <p> - * {@link Tag} is an immutable object that represents the state of a NFC tag at - * the time of discovery. It can be used as a handle to {@link TagTechnology} classes - * to perform advanced operations, or directly queried for its ID via {@link #getId} and the - * set of technologies it contains via {@link #getTechList}. Arrays passed to and - * returned by this class are <em>not</em> cloned, so be careful not to modify them. - * <p> - * A new tag object is created every time a tag is discovered (comes into range), even - * if it is the same physical tag. If a tag is removed and then returned into range, then - * only the most recent tag object can be successfully used to create a {@link TagTechnology}. - * - * <h3>Tag Dispatch</h3> - * When a tag is discovered, a {@link Tag} object is created and passed to a - * single activity via the {@link NfcAdapter#EXTRA_TAG} extra in an - * {@link android.content.Intent} via {@link Context#startActivity}. A four stage dispatch is used - * to select the - * most appropriate activity to handle the tag. The Android OS executes each stage in order, - * and completes dispatch as soon as a single matching activity is found. If there are multiple - * matching activities found at any one stage then the Android activity chooser dialog is shown - * to allow the user to select the activity to receive the tag. - * - * <p>The Tag dispatch mechanism was designed to give a high probability of dispatching - * a tag to the correct activity without showing the user an activity chooser dialog. - * This is important for NFC interactions because they are very transient -- if a user has to - * move the Android device to choose an application then the connection will likely be broken. - * - * <h4>1. Foreground activity dispatch</h4> - * A foreground activity that has called - * {@link NfcAdapter#enableForegroundDispatch NfcAdapter.enableForegroundDispatch()} is - * given priority. See the documentation on - * {@link NfcAdapter#enableForegroundDispatch NfcAdapter.enableForegroundDispatch()} for - * its usage. - * <h4>2. NDEF data dispatch</h4> - * If the tag contains NDEF data the system inspects the first {@link NdefRecord} in the first - * {@link NdefMessage}. If the record is a URI, SmartPoster, or MIME data - * {@link Context#startActivity} is called with {@link NfcAdapter#ACTION_NDEF_DISCOVERED}. For URI - * and SmartPoster records the URI is put into the intent's data field. For MIME records the MIME - * type is put in the intent's type field. This allows activities to register to be launched only - * when data they know how to handle is present on a tag. This is the preferred method of handling - * data on a tag since NDEF data can be stored on many types of tags and doesn't depend on a - * specific tag technology. - * See {@link NfcAdapter#ACTION_NDEF_DISCOVERED} for more detail. If the tag does not contain - * NDEF data, or if no activity is registered - * for {@link NfcAdapter#ACTION_NDEF_DISCOVERED} with a matching data URI or MIME type then dispatch - * moves to stage 3. - * <h4>3. Tag Technology dispatch</h4> - * {@link Context#startActivity} is called with {@link NfcAdapter#ACTION_TECH_DISCOVERED} to - * dispatch the tag to an activity that can handle the technologies present on the tag. - * Technologies are defined as sub-classes of {@link TagTechnology}, see the package - * {@link android.nfc.tech}. The Android OS looks for an activity that can handle one or - * more technologies in the tag. See {@link NfcAdapter#ACTION_TECH_DISCOVERED} for more detail. - * <h4>4. Fall-back dispatch</h4> - * If no activity has been matched then {@link Context#startActivity} is called with - * {@link NfcAdapter#ACTION_TAG_DISCOVERED}. This is intended as a fall-back mechanism. - * See {@link NfcAdapter#ACTION_TAG_DISCOVERED}. - * - * <h3>NFC Tag Background</h3> - * An NFC tag is a passive NFC device, powered by the NFC field of this Android device while - * it is in range. Tag's can come in many forms, such as stickers, cards, key fobs, or - * even embedded in a more sophisticated device. - * <p> - * Tags can have a wide range of capabilities. Simple tags just offer read/write semantics, - * and contain some one time - * programmable areas to make read-only. More complex tags offer math operations - * and per-sector access control and authentication. The most sophisticated tags - * contain operating environments allowing complex interactions with the - * code executing on the tag. Use {@link TagTechnology} classes to access a broad - * range of capabilities available in NFC tags. - * <p> - */ -public final class Tag implements Parcelable { - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - final byte[] mId; - final int[] mTechList; - final String[] mTechStringList; - final Bundle[] mTechExtras; - final int mServiceHandle; // for use by NFC service, 0 indicates a mock - final long mCookie; // for accessibility checking - final INfcTag mTagService; // interface to NFC service, will be null if mock tag - - int mConnectedTechnology; - - /** - * Hidden constructor to be used by NFC service and internal classes. - * @hide - */ - public Tag(byte[] id, int[] techList, Bundle[] techListExtras, int serviceHandle, - long cookie, INfcTag tagService) { - if (techList == null) { - throw new IllegalArgumentException("rawTargets cannot be null"); - } - mId = id; - mTechList = Arrays.copyOf(techList, techList.length); - mTechStringList = generateTechStringList(techList); - // Ensure mTechExtras is as long as mTechList - mTechExtras = Arrays.copyOf(techListExtras, techList.length); - mServiceHandle = serviceHandle; - mCookie = cookie; - mTagService = tagService; - mConnectedTechnology = -1; - - if (tagService == null) { - return; - } - } - - /** - * Construct a mock Tag. - * <p>This is an application constructed tag, so NfcAdapter methods on this Tag may fail - * with {@link IllegalArgumentException} since it does not represent a physical Tag. - * <p>This constructor might be useful for mock testing. - * @param id The tag identifier, can be null - * @param techList must not be null - * @return freshly constructed tag - * @hide - */ - public static Tag createMockTag(byte[] id, int[] techList, Bundle[] techListExtras, - long cookie) { - // set serviceHandle to 0 and tagService to null to indicate mock tag - return new Tag(id, techList, techListExtras, 0, cookie, null); - } - - private String[] generateTechStringList(int[] techList) { - final int size = techList.length; - String[] strings = new String[size]; - for (int i = 0; i < size; i++) { - switch (techList[i]) { - case TagTechnology.ISO_DEP: - strings[i] = IsoDep.class.getName(); - break; - case TagTechnology.MIFARE_CLASSIC: - strings[i] = MifareClassic.class.getName(); - break; - case TagTechnology.MIFARE_ULTRALIGHT: - strings[i] = MifareUltralight.class.getName(); - break; - case TagTechnology.NDEF: - strings[i] = Ndef.class.getName(); - break; - case TagTechnology.NDEF_FORMATABLE: - strings[i] = NdefFormatable.class.getName(); - break; - case TagTechnology.NFC_A: - strings[i] = NfcA.class.getName(); - break; - case TagTechnology.NFC_B: - strings[i] = NfcB.class.getName(); - break; - case TagTechnology.NFC_F: - strings[i] = NfcF.class.getName(); - break; - case TagTechnology.NFC_V: - strings[i] = NfcV.class.getName(); - break; - case TagTechnology.NFC_BARCODE: - strings[i] = NfcBarcode.class.getName(); - break; - default: - throw new IllegalArgumentException("Unknown tech type " + techList[i]); - } - } - return strings; - } - - static int[] getTechCodesFromStrings(String[] techStringList) throws IllegalArgumentException { - if (techStringList == null) { - throw new IllegalArgumentException("List cannot be null"); - } - int[] techIntList = new int[techStringList.length]; - HashMap<String, Integer> stringToCodeMap = getTechStringToCodeMap(); - for (int i = 0; i < techStringList.length; i++) { - Integer code = stringToCodeMap.get(techStringList[i]); - - if (code == null) { - throw new IllegalArgumentException("Unknown tech type " + techStringList[i]); - } - - techIntList[i] = code.intValue(); - } - return techIntList; - } - - private static HashMap<String, Integer> getTechStringToCodeMap() { - HashMap<String, Integer> techStringToCodeMap = new HashMap<String, Integer>(); - - techStringToCodeMap.put(IsoDep.class.getName(), TagTechnology.ISO_DEP); - techStringToCodeMap.put(MifareClassic.class.getName(), TagTechnology.MIFARE_CLASSIC); - techStringToCodeMap.put(MifareUltralight.class.getName(), TagTechnology.MIFARE_ULTRALIGHT); - techStringToCodeMap.put(Ndef.class.getName(), TagTechnology.NDEF); - techStringToCodeMap.put(NdefFormatable.class.getName(), TagTechnology.NDEF_FORMATABLE); - techStringToCodeMap.put(NfcA.class.getName(), TagTechnology.NFC_A); - techStringToCodeMap.put(NfcB.class.getName(), TagTechnology.NFC_B); - techStringToCodeMap.put(NfcF.class.getName(), TagTechnology.NFC_F); - techStringToCodeMap.put(NfcV.class.getName(), TagTechnology.NFC_V); - techStringToCodeMap.put(NfcBarcode.class.getName(), TagTechnology.NFC_BARCODE); - - return techStringToCodeMap; - } - - /** - * For use by NfcService only. - * @hide - */ - @UnsupportedAppUsage - public int getServiceHandle() { - return mServiceHandle; - } - - /** - * For use by NfcService only. - * @hide - */ - public int[] getTechCodeList() { - return mTechList; - } - - /** - * Get the Tag Identifier (if it has one). - * <p>The tag identifier is a low level serial number, used for anti-collision - * and identification. - * <p> Most tags have a stable unique identifier - * (UID), but some tags will generate a random ID every time they are discovered - * (RID), and there are some tags with no ID at all (the byte array will be zero-sized). - * <p> The size and format of an ID is specific to the RF technology used by the tag. - * <p> This function retrieves the ID as determined at discovery time, and does not - * perform any further RF communication or block. - * @return ID as byte array, never null - */ - public byte[] getId() { - return mId; - } - - /** - * Get the technologies available in this tag, as fully qualified class names. - * <p> - * A technology is an implementation of the {@link TagTechnology} interface, - * and can be instantiated by calling the static <code>get(Tag)</code> - * method on the implementation with this Tag. The {@link TagTechnology} - * object can then be used to perform advanced, technology-specific operations on a tag. - * <p> - * Android defines a mandatory set of technologies that must be correctly - * enumerated by all Android NFC devices, and an optional - * set of proprietary technologies. - * See {@link TagTechnology} for more details. - * <p> - * The ordering of the returned array is undefined and should not be relied upon. - * @return an array of fully-qualified {@link TagTechnology} class-names. - */ - public String[] getTechList() { - return mTechStringList; - } - - /** - * Rediscover the technologies available on this tag. - * <p> - * The technologies that are available on a tag may change due to - * operations being performed on a tag. For example, formatting a - * tag as NDEF adds the {@link Ndef} technology. The {@link rediscover} - * method reenumerates the available technologies on the tag - * and returns a new {@link Tag} object containing these technologies. - * <p> - * You may not be connected to any of this {@link Tag}'s technologies - * when calling this method. - * This method guarantees that you will be returned the same Tag - * if it is still in the field. - * <p>May cause RF activity and may block. Must not be called - * from the main application thread. A blocked call will be canceled with - * {@link IOException} by calling {@link #close} from another thread. - * <p>Does not remove power from the RF field, so a tag having a random - * ID should not change its ID. - * @return the rediscovered tag object. - * @throws IOException if the tag cannot be rediscovered - * @hide - */ - // TODO See if we need TagLostException - // TODO Unhide for ICS - // TODO Update documentation to make sure it matches with the final - // implementation. - public Tag rediscover() throws IOException { - if (getConnectedTechnology() != -1) { - throw new IllegalStateException("Close connection to the technology first!"); - } - - if (mTagService == null) { - throw new IOException("Mock tags don't support this operation."); - } - try { - Tag newTag = mTagService.rediscover(getServiceHandle()); - if (newTag != null) { - return newTag; - } else { - throw new IOException("Failed to rediscover tag"); - } - } catch (RemoteException e) { - throw new IOException("NFC service dead"); - } - } - - - /** @hide */ - public boolean hasTech(int techType) { - for (int tech : mTechList) { - if (tech == techType) return true; - } - return false; - } - - /** @hide */ - public Bundle getTechExtras(int tech) { - int pos = -1; - for (int idx = 0; idx < mTechList.length; idx++) { - if (mTechList[idx] == tech) { - pos = idx; - break; - } - } - if (pos < 0) { - return null; - } - - return mTechExtras[pos]; - } - - /** @hide */ - @UnsupportedAppUsage - public INfcTag getTagService() { - if (mTagService == null) { - return null; - } - - try { - if (!mTagService.isTagUpToDate(mCookie)) { - String id_str = ""; - for (int i = 0; i < mId.length; i++) { - id_str = id_str + String.format("%02X ", mId[i]); - } - String msg = "Permission Denial: Tag ( ID: " + id_str + ") is out of date"; - throw new SecurityException(msg); - } - } catch (RemoteException e) { - throw e.rethrowAsRuntimeException(); - } - return mTagService; - } - - /** - * Human-readable description of the tag, for debugging. - */ - @Override - public String toString() { - StringBuilder sb = new StringBuilder("TAG: Tech ["); - String[] techList = getTechList(); - int length = techList.length; - for (int i = 0; i < length; i++) { - sb.append(techList[i]); - if (i < length - 1) { - sb.append(", "); - } - } - sb.append("]"); - return sb.toString(); - } - - /*package*/ static byte[] readBytesWithNull(Parcel in) { - int len = in.readInt(); - byte[] result = null; - if (len >= 0) { - result = new byte[len]; - in.readByteArray(result); - } - return result; - } - - /*package*/ static void writeBytesWithNull(Parcel out, byte[] b) { - if (b == null) { - out.writeInt(-1); - return; - } - out.writeInt(b.length); - out.writeByteArray(b); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - // Null mTagService means this is a mock tag - int isMock = (mTagService == null)?1:0; - - writeBytesWithNull(dest, mId); - dest.writeInt(mTechList.length); - dest.writeIntArray(mTechList); - dest.writeTypedArray(mTechExtras, 0); - dest.writeInt(mServiceHandle); - dest.writeLong(mCookie); - dest.writeInt(isMock); - if (isMock == 0) { - dest.writeStrongBinder(mTagService.asBinder()); - } - } - - public static final @android.annotation.NonNull Parcelable.Creator<Tag> CREATOR = - new Parcelable.Creator<Tag>() { - @Override - public Tag createFromParcel(Parcel in) { - INfcTag tagService; - - // Tag fields - byte[] id = Tag.readBytesWithNull(in); - int[] techList = new int[in.readInt()]; - in.readIntArray(techList); - Bundle[] techExtras = in.createTypedArray(Bundle.CREATOR); - int serviceHandle = in.readInt(); - long cookie = in.readLong(); - int isMock = in.readInt(); - if (isMock == 0) { - tagService = INfcTag.Stub.asInterface(in.readStrongBinder()); - } - else { - tagService = null; - } - - return new Tag(id, techList, techExtras, serviceHandle, cookie, tagService); - } - - @Override - public Tag[] newArray(int size) { - return new Tag[size]; - } - }; - - /** - * For internal use only. - * - * @hide - */ - public synchronized boolean setConnectedTechnology(int technology) { - if (mConnectedTechnology != -1) { - return false; - } - mConnectedTechnology = technology; - return true; - } - - /** - * For internal use only. - * - * @hide - */ - public int getConnectedTechnology() { - return mConnectedTechnology; - } - - /** - * For internal use only. - * - * @hide - */ - public void setTechnologyDisconnected() { - mConnectedTechnology = -1; - } -} diff --git a/nfc/java/android/nfc/TagLostException.java b/nfc/java/android/nfc/TagLostException.java deleted file mode 100644 index 1981d7c0c6e0..000000000000 --- a/nfc/java/android/nfc/TagLostException.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2011, 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 java.io.IOException; - -public class TagLostException extends IOException { - public TagLostException() { - super(); - } - - public TagLostException(String message) { - super(message); - } -} diff --git a/nfc/java/android/nfc/TechListParcel.aidl b/nfc/java/android/nfc/TechListParcel.aidl deleted file mode 100644 index 92e646f220f0..000000000000 --- a/nfc/java/android/nfc/TechListParcel.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2011 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; - -parcelable TechListParcel;
\ No newline at end of file diff --git a/nfc/java/android/nfc/TechListParcel.java b/nfc/java/android/nfc/TechListParcel.java deleted file mode 100644 index 9f01559bd344..000000000000 --- a/nfc/java/android/nfc/TechListParcel.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2011 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.os.Parcel; -import android.os.Parcelable; - -/** @hide */ -public class TechListParcel implements Parcelable { - - private String[][] mTechLists; - - public TechListParcel(String[]... strings) { - mTechLists = strings; - } - - public String[][] getTechLists() { - return mTechLists; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - int count = mTechLists.length; - dest.writeInt(count); - for (int i = 0; i < count; i++) { - String[] techList = mTechLists[i]; - dest.writeStringArray(techList); - } - } - - public static final @android.annotation.NonNull Creator<TechListParcel> CREATOR = new Creator<TechListParcel>() { - @Override - public TechListParcel createFromParcel(Parcel source) { - int count = source.readInt(); - String[][] techLists = new String[count][]; - for (int i = 0; i < count; i++) { - techLists[i] = source.createStringArray(); - } - return new TechListParcel(techLists); - } - - @Override - public TechListParcel[] newArray(int size) { - return new TechListParcel[size]; - } - }; -} diff --git a/nfc/java/android/nfc/TransceiveResult.aidl b/nfc/java/android/nfc/TransceiveResult.aidl deleted file mode 100644 index 98f92ee03eef..000000000000 --- a/nfc/java/android/nfc/TransceiveResult.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2011 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; - -parcelable TransceiveResult; diff --git a/nfc/java/android/nfc/TransceiveResult.java b/nfc/java/android/nfc/TransceiveResult.java deleted file mode 100644 index 7992094e6ba1..000000000000 --- a/nfc/java/android/nfc/TransceiveResult.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2011, 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.os.Parcel; -import android.os.Parcelable; - -import java.io.IOException; - -/** - * Class used to pipe transceive result from the NFC service. - * - * @hide - */ -public final class TransceiveResult implements Parcelable { - public static final int RESULT_SUCCESS = 0; - public static final int RESULT_FAILURE = 1; - public static final int RESULT_TAGLOST = 2; - public static final int RESULT_EXCEEDED_LENGTH = 3; - - final int mResult; - final byte[] mResponseData; - - public TransceiveResult(final int result, final byte[] data) { - mResult = result; - mResponseData = data; - } - - public byte[] getResponseOrThrow() throws IOException { - switch (mResult) { - case RESULT_SUCCESS: - return mResponseData; - case RESULT_TAGLOST: - throw new TagLostException("Tag was lost."); - case RESULT_EXCEEDED_LENGTH: - throw new IOException("Transceive length exceeds supported maximum"); - default: - throw new IOException("Transceive failed"); - } - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mResult); - if (mResult == RESULT_SUCCESS) { - dest.writeInt(mResponseData.length); - dest.writeByteArray(mResponseData); - } - } - - public static final @android.annotation.NonNull Parcelable.Creator<TransceiveResult> CREATOR = - new Parcelable.Creator<TransceiveResult>() { - @Override - public TransceiveResult createFromParcel(Parcel in) { - int result = in.readInt(); - byte[] responseData; - - if (result == RESULT_SUCCESS) { - int responseLength = in.readInt(); - responseData = new byte[responseLength]; - in.readByteArray(responseData); - } else { - responseData = null; - } - return new TransceiveResult(result, responseData); - } - - @Override - public TransceiveResult[] newArray(int size) { - return new TransceiveResult[size]; - } - }; - -} diff --git a/nfc/java/android/nfc/WlcListenerDeviceInfo.aidl b/nfc/java/android/nfc/WlcListenerDeviceInfo.aidl deleted file mode 100644 index 7f2ca545007b..000000000000 --- a/nfc/java/android/nfc/WlcListenerDeviceInfo.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * 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; - -parcelable WlcListenerDeviceInfo; diff --git a/nfc/java/android/nfc/WlcListenerDeviceInfo.java b/nfc/java/android/nfc/WlcListenerDeviceInfo.java deleted file mode 100644 index 45315f812250..000000000000 --- a/nfc/java/android/nfc/WlcListenerDeviceInfo.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * 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; - -import android.annotation.FlaggedApi; -import android.annotation.FloatRange; -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.os.Parcel; -import android.os.Parcelable; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Contains information of the nfc wireless charging listener device information. - */ -@FlaggedApi(Flags.FLAG_ENABLE_NFC_CHARGING) -public final class WlcListenerDeviceInfo implements Parcelable { - /** - * Device is currently not connected with any WlcListenerDevice. - */ - public static final int STATE_DISCONNECTED = 1; - - /** - * Device is currently connected with a WlcListenerDevice and is charging it. - */ - public static final int STATE_CONNECTED_CHARGING = 2; - - /** - * Device is currently connected with a WlcListenerDevice without charging it. - */ - public static final int STATE_CONNECTED_DISCHARGING = 3; - - /** - * Possible states from {@link #getState}. - * @hide - */ - @IntDef(prefix = { "STATE_" }, value = { - STATE_DISCONNECTED, - STATE_CONNECTED_CHARGING, - STATE_CONNECTED_DISCHARGING - }) - @Retention(RetentionPolicy.SOURCE) - public @interface WlcListenerState{} - - private int mProductId; - private double mTemperature; - private double mBatteryLevel; - private int mState; - - /** - * Create a new object containing wlc listener information. - * - * @param productId code for the device vendor - * @param temperature current temperature - * @param batteryLevel current battery level - * @param state current state - */ - public WlcListenerDeviceInfo(int productId, double temperature, double batteryLevel, - @WlcListenerState int state) { - this.mProductId = productId; - this.mTemperature = temperature; - this.mBatteryLevel = batteryLevel; - this.mState = state; - } - - /** - * ProductId of the WLC listener device. - * @return integer that is converted from USI Stylus VendorID[11:0]. - */ - public int getProductId() { - return mProductId; - } - - /** - * Temperature of the WLC listener device. - * @return the value represents the temperature in °C. - */ - public double getTemperature() { - return mTemperature; - } - - /** - * BatteryLevel of the WLC listener device. - * @return battery level in percentage [0-100] - */ - public @FloatRange(from = 0.0, to = 100.0) double getBatteryLevel() { - return mBatteryLevel; - } - - /** - * State of the WLC listener device. - */ - public @WlcListenerState int getState() { - return mState; - } - - private WlcListenerDeviceInfo(Parcel in) { - this.mProductId = in.readInt(); - this.mTemperature = in.readDouble(); - this.mBatteryLevel = in.readDouble(); - this.mState = in.readInt(); - } - - public static final @NonNull Parcelable.Creator<WlcListenerDeviceInfo> CREATOR = - new Parcelable.Creator<WlcListenerDeviceInfo>() { - @Override - public WlcListenerDeviceInfo createFromParcel(Parcel in) { - return new WlcListenerDeviceInfo(in); - } - - @Override - public WlcListenerDeviceInfo[] newArray(int size) { - return new WlcListenerDeviceInfo[size]; - } - }; - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeInt(mProductId); - dest.writeDouble(mTemperature); - dest.writeDouble(mBatteryLevel); - dest.writeInt(mState); - } -} diff --git a/nfc/java/android/nfc/cardemulation/CardEmulation.java b/nfc/java/android/nfc/cardemulation/CardEmulation.java deleted file mode 100644 index fee9c5bfa328..000000000000 --- a/nfc/java/android/nfc/cardemulation/CardEmulation.java +++ /dev/null @@ -1,1497 +0,0 @@ -/* - * Copyright (C) 2013 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.Manifest; -import android.annotation.CallbackExecutor; -import android.annotation.FlaggedApi; -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.RequiresFeature; -import android.annotation.RequiresPermission; -import android.annotation.SdkConstant; -import android.annotation.SdkConstant.SdkConstantType; -import android.annotation.SystemApi; -import android.annotation.UserHandleAware; -import android.annotation.UserIdInt; -import android.app.Activity; -import android.app.role.RoleManager; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.nfc.ComponentNameAndUser; -import android.nfc.Constants; -import android.nfc.Flags; -import android.nfc.INfcCardEmulation; -import android.nfc.INfcEventCallback; -import android.nfc.NfcAdapter; -import android.os.Build; -import android.os.RemoteException; -import android.os.UserHandle; -import android.provider.Settings; -import android.provider.Settings.SettingNotFoundException; -import android.telephony.SubscriptionManager; -import android.util.ArrayMap; -import android.util.Log; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.HashMap; -import java.util.HexFormat; -import java.util.List; -import java.util.Locale; -import java.util.Objects; -import java.util.concurrent.Executor; -import java.util.regex.Pattern; - -/** - * This class can be used to query the state of - * NFC card emulation services. - * - * For a general introduction into NFC card emulation, - * please read the <a href="{@docRoot}guide/topics/connectivity/nfc/hce.html"> - * NFC card emulation developer guide</a>.</p> - * - * <p class="note">Use of this class requires the - * {@link PackageManager#FEATURE_NFC_HOST_CARD_EMULATION} to be present - * on the device. - */ -public final class CardEmulation { - private static final Pattern AID_PATTERN = Pattern.compile("[0-9A-Fa-f]{10,32}\\*?\\#?"); - private static final Pattern PLPF_PATTERN = Pattern.compile("[0-9A-Fa-f,\\?,\\*\\.]*"); - - static final String TAG = "CardEmulation"; - - /** - * Activity action: ask the user to change the default - * card emulation service for a certain category. This will - * show a dialog that asks the user whether they want to - * replace the current default service with the service - * identified with the ComponentName specified in - * {@link #EXTRA_SERVICE_COMPONENT}, for the category - * specified in {@link #EXTRA_CATEGORY}. There is an optional - * extra field using {@link Intent#EXTRA_USER} to specify - * the {@link UserHandle} of the user that owns the app. - * - * @deprecated Please use {@link android.app.role.RoleManager#createRequestRoleIntent(String)} - * with {@link android.app.role.RoleManager#ROLE_WALLET} parameter - * and {@link Activity#startActivityForResult(Intent, int)} instead. - */ - @Deprecated - @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) - public static final String ACTION_CHANGE_DEFAULT = - "android.nfc.cardemulation.action.ACTION_CHANGE_DEFAULT"; - - /** - * The category extra for {@link #ACTION_CHANGE_DEFAULT}. - * - * @see #ACTION_CHANGE_DEFAULT - */ - public static final String EXTRA_CATEGORY = "category"; - - /** - * The service {@link ComponentName} object passed in as an - * extra for {@link #ACTION_CHANGE_DEFAULT}. - * - * @see #ACTION_CHANGE_DEFAULT - */ - public static final String EXTRA_SERVICE_COMPONENT = "component"; - - /** - * Category used for NFC payment services. - */ - public static final String CATEGORY_PAYMENT = "payment"; - - /** - * Category that can be used for all other card emulation - * services. - */ - public static final String CATEGORY_OTHER = "other"; - - /** - * Return value for {@link #getSelectionModeForCategory(String)}. - * - * <p>In this mode, the user has set a default service for this - * category. - * - * <p>When using ISO-DEP card emulation with {@link HostApduService} - * or {@link OffHostApduService}, if a remote NFC device selects - * any of the Application IDs (AIDs) - * that the default service has registered in this category, - * that service will automatically be bound to to handle - * the transaction. - */ - public static final int SELECTION_MODE_PREFER_DEFAULT = 0; - - /** - * Return value for {@link #getSelectionModeForCategory(String)}. - * - * <p>In this mode, when using ISO-DEP card emulation with {@link HostApduService} - * or {@link OffHostApduService}, whenever an Application ID (AID) of this category - * is selected, the user is asked which service they want to use to handle - * the transaction, even if there is only one matching service. - */ - public static final int SELECTION_MODE_ALWAYS_ASK = 1; - - /** - * Return value for {@link #getSelectionModeForCategory(String)}. - * - * <p>In this mode, when using ISO-DEP card emulation with {@link HostApduService} - * or {@link OffHostApduService}, the user will only be asked to select a service - * if the Application ID (AID) selected by the reader has been registered by multiple - * services. If there is only one service that has registered for the AID, - * that service will be invoked directly. - */ - public static final int SELECTION_MODE_ASK_IF_CONFLICT = 2; - /** - * Route to Device Host (DH). - */ - @FlaggedApi(Flags.FLAG_NFC_OVERRIDE_RECOVER_ROUTING_TABLE) - public static final int PROTOCOL_AND_TECHNOLOGY_ROUTE_DH = 0; - /** - * Route to eSE. - */ - @FlaggedApi(Flags.FLAG_NFC_OVERRIDE_RECOVER_ROUTING_TABLE) - public static final int PROTOCOL_AND_TECHNOLOGY_ROUTE_ESE = 1; - /** - * Route to UICC. - */ - @FlaggedApi(Flags.FLAG_NFC_OVERRIDE_RECOVER_ROUTING_TABLE) - public static final int PROTOCOL_AND_TECHNOLOGY_ROUTE_UICC = 2; - - /** - * Route to the default value in config file. - */ - @FlaggedApi(Flags.FLAG_NFC_OVERRIDE_RECOVER_ROUTING_TABLE) - public static final int PROTOCOL_AND_TECHNOLOGY_ROUTE_DEFAULT = 3; - - /** - * Route unset. - */ - @FlaggedApi(Flags.FLAG_NFC_OVERRIDE_RECOVER_ROUTING_TABLE) - public static final int PROTOCOL_AND_TECHNOLOGY_ROUTE_UNSET = -1; - - /** - * Status code returned when {@link #setServiceEnabledForCategoryOther(ComponentName, boolean)} - * succeeded. - * @hide - */ - @SystemApi - @FlaggedApi(Flags.FLAG_NFC_SET_SERVICE_ENABLED_FOR_CATEGORY_OTHER) - public static final int SET_SERVICE_ENABLED_STATUS_OK = 0; - - /** - * Status code returned when {@link #setServiceEnabledForCategoryOther(ComponentName, boolean)} - * failed due to the unsupported feature. - * @hide - */ - @SystemApi - @FlaggedApi(Flags.FLAG_NFC_SET_SERVICE_ENABLED_FOR_CATEGORY_OTHER) - public static final int SET_SERVICE_ENABLED_STATUS_FAILURE_FEATURE_UNSUPPORTED = 1; - - /** - * Status code returned when {@link #setServiceEnabledForCategoryOther(ComponentName, boolean)} - * failed due to the invalid service. - * @hide - */ - @SystemApi - @FlaggedApi(Flags.FLAG_NFC_SET_SERVICE_ENABLED_FOR_CATEGORY_OTHER) - public static final int SET_SERVICE_ENABLED_STATUS_FAILURE_INVALID_SERVICE = 2; - - /** - * Status code returned when {@link #setServiceEnabledForCategoryOther(ComponentName, boolean)} - * failed due to the service is already set to the requested status. - * @hide - */ - @SystemApi - @FlaggedApi(Flags.FLAG_NFC_SET_SERVICE_ENABLED_FOR_CATEGORY_OTHER) - public static final int SET_SERVICE_ENABLED_STATUS_FAILURE_ALREADY_SET = 3; - - /** - * Status code returned when {@link #setServiceEnabledForCategoryOther(ComponentName, boolean)} - * failed due to unknown error. - * @hide - */ - @SystemApi - @FlaggedApi(Flags.FLAG_NFC_SET_SERVICE_ENABLED_FOR_CATEGORY_OTHER) - public static final int SET_SERVICE_ENABLED_STATUS_FAILURE_UNKNOWN_ERROR = 4; - - /** - * Status code returned by {@link #setServiceEnabledForCategoryOther(ComponentName, boolean)} - * @hide - */ - @IntDef(prefix = "SET_SERVICE_ENABLED_STATUS_", value = { - SET_SERVICE_ENABLED_STATUS_OK, - SET_SERVICE_ENABLED_STATUS_FAILURE_FEATURE_UNSUPPORTED, - SET_SERVICE_ENABLED_STATUS_FAILURE_INVALID_SERVICE, - SET_SERVICE_ENABLED_STATUS_FAILURE_ALREADY_SET, - SET_SERVICE_ENABLED_STATUS_FAILURE_UNKNOWN_ERROR - }) - @Retention(RetentionPolicy.SOURCE) - public @interface SetServiceEnabledStatusCode {} - - /** - * Property name used to indicate that an application wants to allow associated services - * to share the same AID routing priority when this application is the role holder. - * <p> - * Example: - * <pre> - * {@code - * <application> - * ... - * <property android:name="android.nfc.cardemulation.PROPERTY_ALLOW_SHARED_ROLE_PRIORITY" - * android:value="true"/> - * </application> - * } - * </pre> - */ - @FlaggedApi(Flags.FLAG_NFC_ASSOCIATED_ROLE_SERVICES) - public static final String PROPERTY_ALLOW_SHARED_ROLE_PRIORITY = - "android.nfc.cardemulation.PROPERTY_ALLOW_SHARED_ROLE_PRIORITY"; - - static boolean sIsInitialized = false; - static HashMap<Context, CardEmulation> sCardEmus = new HashMap<Context, CardEmulation>(); - static INfcCardEmulation sService; - - final Context mContext; - - private CardEmulation(Context context, INfcCardEmulation service) { - mContext = context.getApplicationContext(); - sService = service; - } - - /** - * Helper to get an instance of this class. - * - * @param adapter A reference to an NfcAdapter object. - * @return - */ - public static synchronized CardEmulation getInstance(NfcAdapter adapter) { - if (adapter == null) throw new NullPointerException("NfcAdapter is null"); - Context context = adapter.getContext(); - if (context == null) { - Log.e(TAG, "NfcAdapter context is null."); - throw new UnsupportedOperationException(); - } - if (!sIsInitialized) { - PackageManager pm = context.getPackageManager(); - if (pm == null) { - Log.e(TAG, "Cannot get PackageManager"); - throw new UnsupportedOperationException(); - } - if (!pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) { - Log.e(TAG, "This device does not support card emulation"); - throw new UnsupportedOperationException(); - } - sIsInitialized = true; - } - CardEmulation manager = sCardEmus.get(context); - if (manager == null) { - // Get card emu service - INfcCardEmulation service = adapter.getCardEmulationService(); - if (service == null) { - Log.e(TAG, "This device does not implement the INfcCardEmulation interface."); - throw new UnsupportedOperationException(); - } - manager = new CardEmulation(context, service); - sCardEmus.put(context, manager); - } - return manager; - } - - /** - * Allows an application to query whether a service is currently - * the default service to handle a card emulation category. - * - * <p>Note that if {@link #getSelectionModeForCategory(String)} - * returns {@link #SELECTION_MODE_ALWAYS_ASK} or {@link #SELECTION_MODE_ASK_IF_CONFLICT}, - * this method will always return false. That is because in these - * selection modes a default can't be set at the category level. For categories where - * the selection mode is {@link #SELECTION_MODE_ALWAYS_ASK} or - * {@link #SELECTION_MODE_ASK_IF_CONFLICT}, use - * {@link #isDefaultServiceForAid(ComponentName, String)} to determine whether a service - * is the default for a specific AID. - * - * @param service The ComponentName of the service - * @param category The category - * @return whether service is currently the default service for the category. - * - * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. - */ - public boolean isDefaultServiceForCategory(ComponentName service, String category) { - return callServiceReturn(() -> - sService.isDefaultServiceForCategory( - mContext.getUser().getIdentifier(), service, category), false); - } - - /** - * - * Allows an application to query whether a service is currently - * the default handler for a specified ISO7816-4 Application ID. - * - * @param service The ComponentName of the service - * @param aid The ISO7816-4 Application ID - * @return whether the service is the default handler for the specified AID - * - * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. - */ - public boolean isDefaultServiceForAid(ComponentName service, String aid) { - return callServiceReturn(() -> - sService.isDefaultServiceForAid( - mContext.getUser().getIdentifier(), service, aid), false); - } - - /** - * <p> - * Returns whether the user has allowed AIDs registered in the - * specified category to be handled by a service that is preferred - * by the foreground application, instead of by a pre-configured default. - * - * Foreground applications can set such preferences using the - * {@link #setPreferredService(Activity, ComponentName)} method. - * <p class="note"> - * Starting with {@link Build.VERSION_CODES#VANILLA_ICE_CREAM}, this method will always - * return true. - * - * @param category The category, e.g. {@link #CATEGORY_PAYMENT} - * @return whether AIDs in the category can be handled by a service - * specified by the foreground app. - */ - @SuppressWarnings("NonUserGetterCalled") - public boolean categoryAllowsForegroundPreference(String category) { - Context contextAsUser = mContext.createContextAsUser( - UserHandle.of(UserHandle.myUserId()), 0); - - RoleManager roleManager = contextAsUser.getSystemService(RoleManager.class); - if (roleManager.isRoleAvailable(RoleManager.ROLE_WALLET)) { - return true; - } - - if (CATEGORY_PAYMENT.equals(category)) { - boolean preferForeground = false; - try { - preferForeground = Settings.Secure.getInt( - contextAsUser.getContentResolver(), - Constants.SETTINGS_SECURE_NFC_PAYMENT_FOREGROUND) != 0; - } catch (SettingNotFoundException e) { - } - return preferForeground; - } else { - // Allowed for all other categories - return true; - } - } - - /** - * Returns the service selection mode for the passed in category. - * Valid return values are: - * <p>{@link #SELECTION_MODE_PREFER_DEFAULT} the user has requested a default - * service for this category, which will be preferred. - * <p>{@link #SELECTION_MODE_ALWAYS_ASK} the user has requested to be asked - * every time what service they would like to use in this category. - * <p>{@link #SELECTION_MODE_ASK_IF_CONFLICT} the user will only be asked - * to pick a service if there is a conflict. - * - * <p class="note"> - * Starting with {@link Build.VERSION_CODES#VANILLA_ICE_CREAM}, the default service defined - * by the holder of {@link android.app.role.RoleManager#ROLE_WALLET} and is category agnostic. - * - * @param category The category, for example {@link #CATEGORY_PAYMENT} - * @return the selection mode for the passed in category - */ - public int getSelectionModeForCategory(String category) { - if (CATEGORY_PAYMENT.equals(category)) { - boolean paymentRegistered = callServiceReturn(() -> - sService.isDefaultPaymentRegistered(), false); - if (paymentRegistered) { - return SELECTION_MODE_PREFER_DEFAULT; - } else { - return SELECTION_MODE_ALWAYS_ASK; - } - } else { - return SELECTION_MODE_ASK_IF_CONFLICT; - } - } - /** - * Sets whether when this service becomes the preferred service, if the NFC stack - * should enable observe mode or disable observe mode. The default is to not enable observe - * mode when a service either the foreground default service or the default payment service so - * not calling this method will preserve that behavior. - * - * @param service The component name of the service - * @param enable Whether the service should default to observe mode or not - * @return whether the change was successful. - */ - @FlaggedApi(Flags.FLAG_NFC_OBSERVE_MODE) - public boolean setShouldDefaultToObserveModeForService(@NonNull ComponentName service, - boolean enable) { - return callServiceReturn(() -> - sService.setShouldDefaultToObserveModeForService( - mContext.getUser().getIdentifier(), service, enable), false); - } - - /** - * Register a polling loop filter (PLF) for a HostApduService and indicate whether it should - * auto-transact or not. The PLF can be sequence of an - * even number of at least 2 hexadecimal numbers (0-9, A-F or a-f), representing a series of - * bytes. When non-standard polling loop frame matches this sequence exactly, it may be - * delivered to {@link HostApduService#processPollingFrames(List)}. If auto-transact - * is set to true and this service is currently preferred or there are no other services - * registered for this filter then observe mode will also be disabled. - * @param service The HostApduService to register the filter for - * @param pollingLoopFilter The filter to register - * @param autoTransact true to have the NFC stack automatically disable observe mode and allow - * transactions to proceed when this filter matches, false otherwise - * @return true if the filter was registered, false otherwise - * @throws IllegalArgumentException if the passed in string doesn't parse to at least one byte - */ - @FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP) - public boolean registerPollingLoopFilterForService(@NonNull ComponentName service, - @NonNull String pollingLoopFilter, boolean autoTransact) { - final String pollingLoopFilterV = validatePollingLoopFilter(pollingLoopFilter); - return callServiceReturn(() -> - sService.registerPollingLoopFilterForService( - mContext.getUser().getIdentifier(), service, pollingLoopFilterV, autoTransact), - false); - } - - /** - * Unregister a polling loop filter (PLF) for a HostApduService. If the PLF had previously been - * registered via {@link #registerPollingLoopFilterForService(ComponentName, String, boolean)} - * for this service it will be removed. - * @param service The HostApduService to unregister the filter for - * @param pollingLoopFilter The filter to unregister - * @return true if the filter was removed, false otherwise - * @throws IllegalArgumentException if the passed in string doesn't parse to at least one byte - */ - @FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP) - public boolean removePollingLoopFilterForService(@NonNull ComponentName service, - @NonNull String pollingLoopFilter) { - final String pollingLoopFilterV = validatePollingLoopFilter(pollingLoopFilter); - return callServiceReturn(() -> - sService.removePollingLoopFilterForService( - mContext.getUser().getIdentifier(), service, pollingLoopFilterV), false); - } - - - /** - * Register a polling loop pattern filter (PLPF) for a HostApduService and indicate whether it - * should auto-transact or not. The pattern may include the characters 0-9 and A-F as well as - * the regular expression operators `.`, `?` and `*`. When the beginning of anon-standard - * polling loop frame matches this sequence exactly, it may be delivered to - * {@link HostApduService#processPollingFrames(List)}. If auto-transact is set to true and this - * service is currently preferred or there are no other services registered for this filter - * then observe mode will also be disabled. - * @param service The HostApduService to register the filter for - * @param pollingLoopPatternFilter The pattern filter to register, must to be compatible with - * {@link java.util.regex.Pattern#compile(String)} and only contain hexadecimal numbers - * and `.`, `?` and `*` operators - * @param autoTransact true to have the NFC stack automatically disable observe mode and allow - * transactions to proceed when this filter matches, false otherwise - * @return true if the filter was registered, false otherwise - * @throws IllegalArgumentException if the filter containst elements other than hexadecimal - * numbers and `.`, `?` and `*` operators - * @throws java.util.regex.PatternSyntaxException if the regex syntax is invalid - */ - @FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP) - public boolean registerPollingLoopPatternFilterForService(@NonNull ComponentName service, - @NonNull String pollingLoopPatternFilter, boolean autoTransact) { - final String pollingLoopPatternFilterV = - validatePollingLoopPatternFilter(pollingLoopPatternFilter); - return callServiceReturn(() -> - sService.registerPollingLoopPatternFilterForService( - mContext.getUser().getIdentifier(), service, pollingLoopPatternFilterV, - autoTransact), - false); - } - - /** - * Unregister a polling loop pattern filter (PLPF) for a HostApduService. If the PLF had - * previously been registered via - * {@link #registerPollingLoopFilterForService(ComponentName, String, boolean)} for this - * service it will be removed. - * @param service The HostApduService to unregister the filter for - * @param pollingLoopPatternFilter The filter to unregister, must to be compatible with - * {@link java.util.regex.Pattern#compile(String)} and only contain hexadecimal numbers - * and`.`, `?` and `*` operators - * @return true if the filter was removed, false otherwise - * @throws IllegalArgumentException if the filter containst elements other than hexadecimal - * numbers and `.`, `?` and `*` operators - * @throws java.util.regex.PatternSyntaxException if the regex syntax is invalid - */ - @FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP) - public boolean removePollingLoopPatternFilterForService(@NonNull ComponentName service, - @NonNull String pollingLoopPatternFilter) { - final String pollingLoopPatternFilterV = - validatePollingLoopPatternFilter(pollingLoopPatternFilter); - return callServiceReturn(() -> - sService.removePollingLoopPatternFilterForService( - mContext.getUser().getIdentifier(), service, pollingLoopPatternFilterV), false); - } - - /** - * Registers a list of AIDs for a specific category for the - * specified service. - * - * <p>If a list of AIDs for that category was previously - * registered for this service (either statically - * through the manifest, or dynamically by using this API), - * that list of AIDs will be replaced with this one. - * - * <p>Note that you can only register AIDs for a service that - * is running under the same UID as the caller of this API. Typically - * this means you need to call this from the same - * package as the service itself, though UIDs can also - * be shared between packages using shared UIDs. - * - * @param service The component name of the service - * @param category The category of AIDs to be registered - * @param aids A list containing the AIDs to be registered - * @return whether the registration was successful. - */ - public boolean registerAidsForService(ComponentName service, String category, - List<String> aids) { - final AidGroup aidGroup = new AidGroup(aids, category); - return callServiceReturn(() -> - sService.registerAidGroupForService( - mContext.getUser().getIdentifier(), service, aidGroup), false); - } - - /** - * Unsets the off-host Secure Element for the given service. - * - * <p>Note that this will only remove Secure Element that was dynamically - * set using the {@link #setOffHostForService(ComponentName, String)} - * and resets it to a value that was statically assigned using manifest. - * - * <p>Note that you can only unset off-host SE for a service that - * is running under the same UID as the caller of this API. Typically - * this means you need to call this from the same - * package as the service itself, though UIDs can also - * be shared between packages using shared UIDs. - * - * @param service The component name of the service - * @return whether the registration was successful. - */ - @RequiresPermission(android.Manifest.permission.NFC) - @NonNull - public boolean unsetOffHostForService(@NonNull ComponentName service) { - return callServiceReturn(() -> - sService.unsetOffHostForService( - mContext.getUser().getIdentifier(), service), false); - } - - /** - * Sets the off-host Secure Element for the given service. - * - * <p>If off-host SE was initially set (either statically - * through the manifest, or dynamically by using this API), - * it will be replaced with this one. All AIDs registered by - * this service will be re-routed to this Secure Element if - * successful. AIDs that was statically assigned using manifest - * will re-route to off-host SE that stated in manifest after NFC - * toggle. - * - * <p>Note that you can only set off-host SE for a service that - * is running under the same UID as the caller of this API. Typically - * this means you need to call this from the same - * package as the service itself, though UIDs can also - * be shared between packages using shared UIDs. - * - * <p>Registeration will be successful only if the Secure Element - * exists on the device. - * - * @param service The component name of the service - * @param offHostSecureElement Secure Element to register the AID to. Only accept strings with - * prefix SIM or prefix eSE. - * Ref: GSMA TS.26 - NFC Handset Requirements - * TS26_NFC_REQ_069: For UICC, Secure Element Name SHALL be - * SIM[smartcard slot] - * (e.g. SIM/SIM1, SIM2… SIMn). - * TS26_NFC_REQ_070: For embedded SE, Secure Element Name SHALL be - * eSE[number] - * (e.g. eSE/eSE1, eSE2, etc.). - * @return whether the registration was successful. - */ - @RequiresPermission(android.Manifest.permission.NFC) - @NonNull - public boolean setOffHostForService(@NonNull ComponentName service, - @NonNull String offHostSecureElement) { - NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext); - if (adapter == null || offHostSecureElement == null) { - return false; - } - - List<String> validSE = adapter.getSupportedOffHostSecureElements(); - if ((offHostSecureElement.startsWith("eSE") && !validSE.contains("eSE")) - || (offHostSecureElement.startsWith("SIM") && !validSE.contains("SIM"))) { - return false; - } - - if (!offHostSecureElement.startsWith("eSE") && !offHostSecureElement.startsWith("SIM")) { - return false; - } - - if (offHostSecureElement.equals("eSE")) { - offHostSecureElement = "eSE1"; - } else if (offHostSecureElement.equals("SIM")) { - offHostSecureElement = "SIM1"; - } - final String offHostSecureElementV = new String(offHostSecureElement); - return callServiceReturn(() -> - sService.setOffHostForService( - mContext.getUser().getIdentifier(), service, offHostSecureElementV), false); - } - - /** - * Retrieves the currently registered AIDs for the specified - * category for a service. - * - * <p>Note that this will only return AIDs that were dynamically - * registered using {@link #registerAidsForService(ComponentName, String, List)} - * method. It will *not* return AIDs that were statically registered - * in the manifest. - * - * @param service The component name of the service - * @param category The category for which the AIDs were registered, - * e.g. {@link #CATEGORY_PAYMENT} - * @return The list of AIDs registered for this category, or null if it couldn't be found. - */ - public List<String> getAidsForService(ComponentName service, String category) { - AidGroup group = callServiceReturn(() -> - sService.getAidGroupForService( - mContext.getUser().getIdentifier(), service, category), null); - return (group != null ? group.getAids() : null); - } - - /** - * Removes a previously registered list of AIDs for the specified category for the - * service provided. - * - * <p>Note that this will only remove AIDs that were dynamically - * registered using the {@link #registerAidsForService(ComponentName, String, List)} - * method. It will *not* remove AIDs that were statically registered in - * the manifest. If dynamically registered AIDs are removed using - * this method, and a statically registered AID group for the same category - * exists in the manifest, the static AID group will become active again. - * - * @param service The component name of the service - * @param category The category of the AIDs to be removed, e.g. {@link #CATEGORY_PAYMENT} - * @return whether the group was successfully removed. - */ - public boolean removeAidsForService(ComponentName service, String category) { - return callServiceReturn(() -> - sService.removeAidGroupForService( - mContext.getUser().getIdentifier(), service, category), false); - } - - /** - * Allows a foreground application to specify which card emulation service - * should be preferred while a specific Activity is in the foreground. - * - * <p>The specified Activity must currently be in resumed state. A good - * paradigm is to call this method in your {@link Activity#onResume}, and to call - * {@link #unsetPreferredService(Activity)} in your {@link Activity#onPause}. - * - * <p>This method call will fail in two specific scenarios: - * <ul> - * <li> If the service registers one or more AIDs in the {@link #CATEGORY_PAYMENT} - * category, but the user has indicated that foreground apps are not allowed - * to override the default payment service. - * <li> If the service registers one or more AIDs in the {@link #CATEGORY_OTHER} - * category that are also handled by the default payment service, and the - * user has indicated that foreground apps are not allowed to override the - * default payment service. - * </ul> - * - * <p> Use {@link #categoryAllowsForegroundPreference(String)} to determine - * whether foreground apps can override the default payment service. - * - * <p>Note that this preference is not persisted by the OS, and hence must be - * called every time the Activity is resumed. - * - * @param activity The activity which prefers this service to be invoked - * @param service The service to be preferred while this activity is in the foreground - * @return whether the registration was successful - */ - public boolean setPreferredService(Activity activity, ComponentName service) { - // Verify the activity is in the foreground before calling into NfcService - if (activity == null || service == null) { - throw new NullPointerException("activity or service or category is null"); - } - return callServiceReturn(() -> sService.setPreferredService(service), false); - } - - /** - * Unsets the preferred service for the specified Activity. - * - * <p>Note that the specified Activity must still be in resumed - * state at the time of this call. A good place to call this method - * is in your {@link Activity#onPause} implementation. - * - * @param activity The activity which the service was registered for - * @return true when successful - */ - public boolean unsetPreferredService(Activity activity) { - if (activity == null) { - throw new NullPointerException("activity is null"); - } - return callServiceReturn(() -> sService.unsetPreferredService(), false); - } - - /** - * Some devices may allow an application to register all - * AIDs that starts with a certain prefix, e.g. - * "A000000004*" to register all MasterCard AIDs. - * - * Use this method to determine whether this device - * supports registering AID prefixes. - * - * @return whether AID prefix registering is supported on this device. - */ - public boolean supportsAidPrefixRegistration() { - return callServiceReturn(() -> sService.supportsAidPrefixRegistration(), false); - } - - /** - * Retrieves the registered AIDs for the preferred payment service. - * - * @return The list of AIDs registered for this category, or null if it couldn't be found. - */ - @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) - @Nullable - public List<String> getAidsForPreferredPaymentService() { - ApduServiceInfo serviceInfo = callServiceReturn(() -> - sService.getPreferredPaymentService(mContext.getUser().getIdentifier()), null); - return (serviceInfo != null ? serviceInfo.getAids() : null); - } - - /** - * Retrieves the route destination for the preferred payment service. - * - * <p class="note"> - * Starting with {@link Build.VERSION_CODES#VANILLA_ICE_CREAM}, the preferred payment service - * no longer exists and is replaced by {@link android.app.role.RoleManager#ROLE_WALLET}. This - * will return the route for one of the services registered by the role holder (if any). If - * there are multiple services registered, it is unspecified which of those will be used to - * determine the route. - * - * @return The route destination secure element name of the preferred payment service. - * HCE payment: "Host" - * OffHost payment: 1. String with prefix SIM or prefix eSE string. - * Ref: GSMA TS.26 - NFC Handset Requirements - * TS26_NFC_REQ_069: For UICC, Secure Element Name SHALL be - * SIM[smartcard slot] - * (e.g. SIM/SIM1, SIM2… SIMn). - * TS26_NFC_REQ_070: For embedded SE, Secure Element Name SHALL be - * eSE[number] - * (e.g. eSE/eSE1, eSE2, etc.). - * 2. "OffHost" if the payment service does not specify secure element - * name. - */ - @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) - @Nullable - public String getRouteDestinationForPreferredPaymentService() { - ApduServiceInfo serviceInfo = callServiceReturn(() -> - sService.getPreferredPaymentService(mContext.getUser().getIdentifier()), null); - if (serviceInfo != null) { - if (!serviceInfo.isOnHost()) { - return serviceInfo.getOffHostSecureElement() == null ? - "OffHost" : serviceInfo.getOffHostSecureElement(); - } - return "Host"; - } - return null; - } - - /** - * Returns a user-visible description of the preferred payment service. - * - * <p class="note"> - * Starting with {@link Build.VERSION_CODES#VANILLA_ICE_CREAM}, the preferred payment service - * no longer exists and is replaced by {@link android.app.role.RoleManager#ROLE_WALLET}. This - * will return the description for one of the services registered by the role holder (if any). - * If there are multiple services registered, it is unspecified which of those will be used - * to obtain the service description here. - * - * @return the preferred payment service description - */ - @RequiresPermission(Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) - @Nullable - public CharSequence getDescriptionForPreferredPaymentService() { - ApduServiceInfo serviceInfo = callServiceReturn(() -> - sService.getPreferredPaymentService(mContext.getUser().getIdentifier()), null); - return (serviceInfo != null ? serviceInfo.getDescription() : null); - } - - /** - * @hide - */ - public boolean setDefaultServiceForCategory(ComponentName service, String category) { - return callServiceReturn(() -> - sService.setDefaultServiceForCategory( - mContext.getUser().getIdentifier(), service, category), false); - } - - /** - * @hide - */ - public boolean setDefaultForNextTap(ComponentName service) { - return callServiceReturn(() -> - sService.setDefaultForNextTap( - mContext.getUser().getIdentifier(), service), false); - } - - /** - * @hide - */ - public boolean setDefaultForNextTap(int userId, ComponentName service) { - return callServiceReturn(() -> - sService.setDefaultForNextTap(userId, service), false); - } - - /** - * @hide - */ - public List<ApduServiceInfo> getServices(String category) { - return callServiceReturn(() -> - sService.getServices( - mContext.getUser().getIdentifier(), category), null); - } - - /** - * Retrieves list of services registered of the provided category for the provided user. - * - * @param category Category string, one of {@link #CATEGORY_PAYMENT} or {@link #CATEGORY_OTHER} - * @param userId the user handle of the user whose information is being requested. - * @hide - */ - @SystemApi - @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - @NonNull - public List<ApduServiceInfo> getServices(@NonNull String category, @UserIdInt int userId) { - return callServiceReturn(() -> - sService.getServices(userId, category), null); - } - - /** - * Tests the validity of the polling loop filter. - * @param pollingLoopFilter The polling loop filter to test. - * - * @hide - */ - @FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP) - public static @NonNull String validatePollingLoopFilter(@NonNull String pollingLoopFilter) { - // Verify hex characters - byte[] plfBytes = HexFormat.of().parseHex(pollingLoopFilter); - if (plfBytes.length == 0) { - throw new IllegalArgumentException( - "Polling loop filter must contain at least one byte."); - } - return HexFormat.of().withUpperCase().formatHex(plfBytes); - } - - /** - * Tests the validity of the polling loop pattern filter. - * @param pollingLoopPatternFilter The polling loop filter to test. - * - * @hide - */ - @FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP) - public static @NonNull String validatePollingLoopPatternFilter( - @NonNull String pollingLoopPatternFilter) { - // Verify hex characters - if (!PLPF_PATTERN.matcher(pollingLoopPatternFilter).matches()) { - throw new IllegalArgumentException( - "Polling loop pattern filters may only contain hexadecimal numbers, ?s and *s"); - } - return Pattern.compile(pollingLoopPatternFilter.toUpperCase(Locale.ROOT)).toString(); - } - - /** - * A valid AID according to ISO/IEC 7816-4: - * <ul> - * <li>Has >= 5 bytes and <=16 bytes (>=10 hex chars and <= 32 hex chars) - * <li>Consist of only hex characters - * <li>Additionally, we allow an asterisk at the end, to indicate - * a prefix - * <li>Additinally we allow an (#) at symbol at the end, to indicate - * a subset - * </ul> - * - * @hide - */ - public static boolean isValidAid(String aid) { - if (aid == null) - return false; - - // If a prefix/subset AID, the total length must be odd (even # of AID chars + '*') - if ((aid.endsWith("*") || aid.endsWith("#")) && ((aid.length() % 2) == 0)) { - Log.e(TAG, "AID " + aid + " is not a valid AID."); - return false; - } - - // If not a prefix/subset AID, the total length must be even (even # of AID chars) - if ((!(aid.endsWith("*") || aid.endsWith("#"))) && ((aid.length() % 2) != 0)) { - Log.e(TAG, "AID " + aid + " is not a valid AID."); - return false; - } - - // Verify hex characters - if (!AID_PATTERN.matcher(aid).matches()) { - Log.e(TAG, "AID " + aid + " is not a valid AID."); - return false; - } - - return true; - } - - /** - * Allows to set or unset preferred service (category other) to avoid AID Collision. The user - * should use corresponding context using {@link Context#createContextAsUser(UserHandle, int)} - * - * @param service The ComponentName of the service - * @param status true to enable, false to disable - * @return status code defined in {@link SetServiceEnabledStatusCode} - * - * @hide - */ - @SystemApi - @FlaggedApi(Flags.FLAG_NFC_SET_SERVICE_ENABLED_FOR_CATEGORY_OTHER) - @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - @SetServiceEnabledStatusCode - public int setServiceEnabledForCategoryOther(@NonNull ComponentName service, - boolean status) { - return callServiceReturn(() -> - sService.setServiceEnabledForCategoryOther(mContext.getUser().getIdentifier(), - service, status), SET_SERVICE_ENABLED_STATUS_FAILURE_UNKNOWN_ERROR); - } - - /** @hide */ - @IntDef(prefix = "PROTOCOL_AND_TECHNOLOGY_ROUTE_", - value = { - PROTOCOL_AND_TECHNOLOGY_ROUTE_DH, - PROTOCOL_AND_TECHNOLOGY_ROUTE_ESE, - PROTOCOL_AND_TECHNOLOGY_ROUTE_UICC, - PROTOCOL_AND_TECHNOLOGY_ROUTE_UNSET, - PROTOCOL_AND_TECHNOLOGY_ROUTE_DEFAULT - }) - @Retention(RetentionPolicy.SOURCE) - public @interface ProtocolAndTechnologyRoute {} - - /** - * Setting NFC controller routing table, which includes Protocol Route and Technology Route, - * while this Activity is in the foreground. - * - * The parameter set to {@link #PROTOCOL_AND_TECHNOLOGY_ROUTE_UNSET} - * can be used to keep current values for that entry. Either - * Protocol Route or Technology Route should be override when calling this API, otherwise - * throw {@link IllegalArgumentException}. - * <p> - * Example usage in an Activity that requires to set proto route to "ESE" and keep tech route: - * <pre> - * protected void onResume() { - * mNfcAdapter.overrideRoutingTable( - * this, {@link #PROTOCOL_AND_TECHNOLOGY_ROUTE_ESE}, - * {@link #PROTOCOL_AND_TECHNOLOGY_ROUTE_UNSET}); - * }</pre> - * </p> - * Also activities must call {@link #recoverRoutingTable(Activity)} - * when it goes to the background. Only the package of the - * currently preferred service (the service set as preferred by the current foreground - * application via {@link CardEmulation#setPreferredService(Activity, ComponentName)} or the - * current Default Wallet Role Holder {@link RoleManager#ROLE_WALLET}), - * otherwise a call to this method will fail and throw {@link SecurityException}. - * @param activity The Activity that requests NFC controller routing table to be changed. - * @param protocol ISO-DEP route destination, where the possible inputs are defined - * in {@link ProtocolAndTechnologyRoute}. - * @param technology Tech-A, Tech-B and Tech-F route destination, where the possible inputs - * are defined in {@link ProtocolAndTechnologyRoute} - * @throws SecurityException if the caller is not the preferred NFC service - * @throws IllegalArgumentException if the activity is not resumed or the caller is not in the - * foreground. - * <p> - * This is a high risk API and only included to support mainline effort - * @hide - */ - @SystemApi - @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - @FlaggedApi(Flags.FLAG_NFC_OVERRIDE_RECOVER_ROUTING_TABLE) - public void overrideRoutingTable( - @NonNull Activity activity, @ProtocolAndTechnologyRoute int protocol, - @ProtocolAndTechnologyRoute int technology) { - if (!activity.isResumed()) { - throw new IllegalArgumentException("Activity must be resumed."); - } - String protocolRoute = routeIntToString(protocol); - String technologyRoute = routeIntToString(technology); - callService(() -> - sService.overrideRoutingTable( - mContext.getUser().getIdentifier(), - protocolRoute, - technologyRoute, - mContext.getPackageName())); - } - - /** - * Restore the NFC controller routing table, - * which was changed by {@link #overrideRoutingTable(Activity, int, int)} - * - * @param activity The Activity that requested NFC controller routing table to be changed. - * @throws IllegalArgumentException if the caller is not in the foreground. - * - * @hide - */ - @SystemApi - @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - @FlaggedApi(Flags.FLAG_NFC_OVERRIDE_RECOVER_ROUTING_TABLE) - public void recoverRoutingTable(@NonNull Activity activity) { - if (!activity.isResumed()) { - throw new IllegalArgumentException("Activity must be resumed."); - } - callService(() -> - sService.recoverRoutingTable( - mContext.getUser().getIdentifier())); - } - - /** - * Is EUICC supported as a Secure Element EE which supports off host card emulation. - * - * @return true if the device supports EUICC for off host card emulation, false otherwise. - */ - @FlaggedApi(android.nfc.Flags.FLAG_ENABLE_CARD_EMULATION_EUICC) - public boolean isEuiccSupported() { - return callServiceReturn(() -> sService.isEuiccSupported(), false); - } - - /** - * Setting the default subscription ID succeeded. - * @hide - */ - @SystemApi - @FlaggedApi(android.nfc.Flags.FLAG_ENABLE_CARD_EMULATION_EUICC) - public static final int SET_SUBSCRIPTION_ID_STATUS_SUCCESS = 0; - - /** - * Setting the default subscription ID failed because the subscription ID is invalid. - * @hide - */ - @SystemApi - @FlaggedApi(android.nfc.Flags.FLAG_ENABLE_CARD_EMULATION_EUICC) - public static final int SET_SUBSCRIPTION_ID_STATUS_FAILED_INVALID_SUBSCRIPTION_ID = 1; - - /** - * Setting the default subscription ID failed because there was an internal error processing - * the request. For ex: NFC service died in the middle of handling the API. - * @hide - */ - @SystemApi - @FlaggedApi(android.nfc.Flags.FLAG_ENABLE_CARD_EMULATION_EUICC) - public static final int SET_SUBSCRIPTION_ID_STATUS_FAILED_INTERNAL_ERROR = 2; - - /** - * Setting the default subscription ID failed because this feature is not supported on the - * device. - * @hide - */ - @SystemApi - @FlaggedApi(android.nfc.Flags.FLAG_ENABLE_CARD_EMULATION_EUICC) - public static final int SET_SUBSCRIPTION_ID_STATUS_FAILED_NOT_SUPPORTED = 3; - - /** - * Setting the default subscription ID failed because of unknown error. - * @hide - */ - @SystemApi - @FlaggedApi(Flags.FLAG_ENABLE_CARD_EMULATION_EUICC) - public static final int SET_SUBSCRIPTION_ID_STATUS_UNKNOWN = -1; - - /** @hide */ - @IntDef(prefix = "SET_SUBSCRIPTION_ID_STATUS_", - value = { - SET_SUBSCRIPTION_ID_STATUS_SUCCESS, - SET_SUBSCRIPTION_ID_STATUS_FAILED_INVALID_SUBSCRIPTION_ID, - SET_SUBSCRIPTION_ID_STATUS_FAILED_INTERNAL_ERROR, - SET_SUBSCRIPTION_ID_STATUS_FAILED_NOT_SUPPORTED, - SET_SUBSCRIPTION_ID_STATUS_UNKNOWN - }) - @Retention(RetentionPolicy.SOURCE) - public @interface SetSubscriptionIdStatus {} - - /** - * Sets the system's default NFC subscription id. - * - * <p> For devices with multiple UICC/EUICC that is configured to be NFCEE, this sets the - * default UICC NFCEE that will handle NFC offhost CE transactions </p> - * - * @param subscriptionId the default NFC subscription Id to set. User can get subscription id - * from {@link SubscriptionManager#getSubscriptionId(int)} - * @return status of the operation. - * - * @throws UnsupportedOperationException If the device does not have - * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. - * @hide - */ - @SystemApi - @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) - @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - @FlaggedApi(android.nfc.Flags.FLAG_ENABLE_CARD_EMULATION_EUICC) - public @SetSubscriptionIdStatus int setDefaultNfcSubscriptionId(int subscriptionId) { - return callServiceReturn(() -> - sService.setDefaultNfcSubscriptionId( - subscriptionId, mContext.getPackageName()), - SET_SUBSCRIPTION_ID_STATUS_FAILED_INTERNAL_ERROR); - } - - /** - * Returns the system's default NFC subscription id. - * - * <p> For devices with multiple UICC/EUICC that is configured to be NFCEE, this returns the - * default UICC NFCEE that will handle NFC offhost CE transactions </p> - * <p> If the device has no UICC that can serve as NFCEE, this will return - * {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}.</p> - * - * @return the default NFC subscription Id if set, - * {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID} otherwise. - * - * @throws UnsupportedOperationException If the device does not have - * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. - */ - @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) - @FlaggedApi(android.nfc.Flags.FLAG_ENABLE_CARD_EMULATION_EUICC) - public int getDefaultNfcSubscriptionId() { - return callServiceReturn(() -> - sService.getDefaultNfcSubscriptionId(mContext.getPackageName()), - SubscriptionManager.INVALID_SUBSCRIPTION_ID); - } - - /** - * Returns the value of {@link Settings.Secure#NFC_PAYMENT_DEFAULT_COMPONENT}. - * - * @param context A context - * @return A ComponentName for the setting value, or null. - * - * @hide - */ - @SystemApi - @UserHandleAware - @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) - @SuppressWarnings("AndroidFrameworkClientSidePermissionCheck") - @FlaggedApi(android.permission.flags.Flags.FLAG_WALLET_ROLE_ENABLED) - @Nullable - public static ComponentName getPreferredPaymentService(@NonNull Context context) { - context.checkCallingOrSelfPermission(Manifest.permission.NFC_PREFERRED_PAYMENT_INFO); - String defaultPaymentComponent = Settings.Secure.getString(context.getContentResolver(), - Constants.SETTINGS_SECURE_NFC_PAYMENT_DEFAULT_COMPONENT); - - if (defaultPaymentComponent == null) { - return null; - } - - return ComponentName.unflattenFromString(defaultPaymentComponent); - } - - /** @hide */ - interface ServiceCall { - void call() throws RemoteException; - } - /** @hide */ - public static void callService(ServiceCall call) { - try { - if (sService == null) { - NfcAdapter.attemptDeadServiceRecovery( - new RemoteException("NFC CardEmulation Service is null")); - sService = NfcAdapter.getCardEmulationService(); - } - call.call(); - } catch (RemoteException e) { - NfcAdapter.attemptDeadServiceRecovery(e); - sService = NfcAdapter.getCardEmulationService(); - try { - call.call(); - } catch (RemoteException ee) { - ee.rethrowAsRuntimeException(); - } - } - } - /** @hide */ - interface ServiceCallReturn<T> { - T call() throws RemoteException; - } - /** @hide */ - public static <T> T callServiceReturn(ServiceCallReturn<T> call, T defaultReturn) { - try { - if (sService == null) { - NfcAdapter.attemptDeadServiceRecovery( - new RemoteException("NFC CardEmulation Service is null")); - sService = NfcAdapter.getCardEmulationService(); - } - return call.call(); - } catch (RemoteException e) { - NfcAdapter.attemptDeadServiceRecovery(e); - sService = NfcAdapter.getCardEmulationService(); - // Try one more time - try { - return call.call(); - } catch (RemoteException ee) { - ee.rethrowAsRuntimeException(); - } - } - return defaultReturn; - } - - /** @hide */ - public static String routeIntToString(@ProtocolAndTechnologyRoute int route) { - return switch (route) { - case PROTOCOL_AND_TECHNOLOGY_ROUTE_DH -> "DH"; - case PROTOCOL_AND_TECHNOLOGY_ROUTE_ESE -> "eSE"; - case PROTOCOL_AND_TECHNOLOGY_ROUTE_UICC -> "SIM"; - case PROTOCOL_AND_TECHNOLOGY_ROUTE_UNSET -> null; - case PROTOCOL_AND_TECHNOLOGY_ROUTE_DEFAULT -> "default"; - default -> throw new IllegalStateException("Unexpected value: " + route); - }; - } - - @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER) - public static final int NFC_INTERNAL_ERROR_UNKNOWN = 0; - - /** - * This error is reported when the NFC command watchdog restarts the NFC stack. - */ - @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER) - public static final int NFC_INTERNAL_ERROR_NFC_CRASH_RESTART = 1; - - /** - * This error is reported when the NFC controller does not respond or there's an NCI transport - * error. - */ - @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER) - public static final int NFC_INTERNAL_ERROR_NFC_HARDWARE_ERROR = 2; - - /** - * This error is reported when the NFC stack times out while waiting for a response to a command - * sent to the NFC hardware. - */ - @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER) - public static final int NFC_INTERNAL_ERROR_COMMAND_TIMEOUT = 3; - - /** @hide */ - @Retention(RetentionPolicy.SOURCE) - @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER) - @IntDef(prefix = "NFC_INTERNAL_ERROR_", value = { - NFC_INTERNAL_ERROR_UNKNOWN, - NFC_INTERNAL_ERROR_NFC_CRASH_RESTART, - NFC_INTERNAL_ERROR_NFC_HARDWARE_ERROR, - NFC_INTERNAL_ERROR_COMMAND_TIMEOUT, - }) - public @interface NfcInternalErrorType {} - - /** Listener for preferred service state changes. */ - @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER) - public interface NfcEventCallback { - /** - * This method is called when this package gains or loses preferred Nfc service status, - * either the Default Wallet Role holder (see {@link - * android.app.role.RoleManager#ROLE_WALLET}) or the preferred service of the foreground - * activity set with {@link #setPreferredService(Activity, ComponentName)} - * - * @param isPreferred true is this service has become the preferred Nfc service, false if it - * is no longer the preferred service - */ - @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER) - default void onPreferredServiceChanged(boolean isPreferred) {} - - /** - * This method is called when observe mode has been enabled or disabled. - * - * @param isEnabled true if observe mode has been enabled, false if it has been disabled - */ - @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER) - default void onObserveModeStateChanged(boolean isEnabled) {} - - /** - * This method is called when an AID conflict is detected during an NFC transaction. This - * can happen when multiple services are registered for the same AID. If your service is - * registered for this AID you may want to instruct users to bring your app to the - * foreground and ensure you call {@link #setPreferredService(Activity, ComponentName)} - * to ensure the transaction is routed to your service. - * - * @param aid The AID that is in conflict - */ - @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER) - default void onAidConflictOccurred(@NonNull String aid) {} - - /** - * This method is called when an AID is not routed to any service during an NFC - * transaction. This can happen when no service is registered for the given AID. - * - * @param aid the AID that was not routed - */ - @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER) - default void onAidNotRouted(@NonNull String aid) {} - - /** - * This method is called when the NFC state changes. - * - * @see NfcAdapter#getAdapterState() - * - * @param state The new NFC state - */ - @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER) - default void onNfcStateChanged(@NfcAdapter.AdapterState int state) {} - /** - * This method is called when the NFC controller is in card emulation mode and an NFC - * reader's field is either detected or lost. - * - * @param isDetected true if an NFC reader is detected, false if it is lost - */ - @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER) - default void onRemoteFieldChanged(boolean isDetected) {} - - /** - * This method is called when an internal error is reported by the NFC stack. - * - * No action is required in response to these events as the NFC stack will automatically - * attempt to recover. These errors are reported for informational purposes only. - * - * Note that these errors can be reported when performing various internal NFC operations - * (such as during device shutdown) and cannot always be explicitly correlated with NFC - * transaction failures. - * - * @param errorType The type of the internal error - */ - @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER) - default void onInternalErrorReported(@NfcInternalErrorType int errorType) {} - } - - private final ArrayMap<NfcEventCallback, Executor> mNfcEventCallbacks = new ArrayMap<>(); - - final INfcEventCallback mINfcEventCallback = - new INfcEventCallback.Stub() { - public void onPreferredServiceChanged(ComponentNameAndUser componentNameAndUser) { - if (!android.nfc.Flags.nfcEventListener()) { - return; - } - boolean isPreferred = - componentNameAndUser != null - && componentNameAndUser.getUserId() - == mContext.getUser().getIdentifier() - && componentNameAndUser.getComponentName() != null - && Objects.equals( - mContext.getPackageName(), - componentNameAndUser.getComponentName() - .getPackageName()); - callListeners(listener -> listener.onPreferredServiceChanged(isPreferred)); - } - - public void onObserveModeStateChanged(boolean isEnabled) { - if (!android.nfc.Flags.nfcEventListener()) { - return; - } - callListeners(listener -> listener.onObserveModeStateChanged(isEnabled)); - } - - public void onAidConflictOccurred(String aid) { - if (!android.nfc.Flags.nfcEventListener()) { - return; - } - callListeners(listener -> listener.onAidConflictOccurred(aid)); - } - - public void onAidNotRouted(String aid) { - if (!android.nfc.Flags.nfcEventListener()) { - return; - } - callListeners(listener -> listener.onAidNotRouted(aid)); - } - - public void onNfcStateChanged(int state) { - if (!android.nfc.Flags.nfcEventListener()) { - return; - } - callListeners(listener -> listener.onNfcStateChanged(state)); - } - - public void onRemoteFieldChanged(boolean isDetected) { - if (!android.nfc.Flags.nfcEventListener()) { - return; - } - callListeners(listener -> listener.onRemoteFieldChanged(isDetected)); - } - - public void onInternalErrorReported(@NfcInternalErrorType int errorType) { - if (!android.nfc.Flags.nfcEventListener()) { - return; - } - callListeners(listener -> listener.onInternalErrorReported(errorType)); - } - - interface ListenerCall { - void invoke(NfcEventCallback listener); - } - - private void callListeners(ListenerCall listenerCall) { - synchronized (mNfcEventCallbacks) { - mNfcEventCallbacks.forEach( - (listener, executor) -> { - executor.execute(() -> listenerCall.invoke(listener)); - }); - } - } - }; - - /** - * Register a listener for NFC Events. - * - * @param executor The Executor to run the call back with - * @param listener The listener to register - */ - @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER) - public void registerNfcEventCallback( - @NonNull @CallbackExecutor Executor executor, @NonNull NfcEventCallback listener) { - if (!android.nfc.Flags.nfcEventListener()) { - return; - } - synchronized (mNfcEventCallbacks) { - mNfcEventCallbacks.put(listener, executor); - if (mNfcEventCallbacks.size() == 1) { - callService(() -> sService.registerNfcEventCallback(mINfcEventCallback)); - } - } - } - - /** - * Unregister a preferred service listener that was previously registered with {@link - * #registerNfcEventCallback(Executor, NfcEventCallback)} - * - * @param listener The previously registered listener to unregister - */ - @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER) - public void unregisterNfcEventCallback(@NonNull NfcEventCallback listener) { - if (!android.nfc.Flags.nfcEventListener()) { - return; - } - synchronized (mNfcEventCallbacks) { - mNfcEventCallbacks.remove(listener); - if (mNfcEventCallbacks.size() == 0) { - callService(() -> sService.unregisterNfcEventCallback(mINfcEventCallback)); - } - } - } -} diff --git a/nfc/java/android/nfc/cardemulation/HostApduService.java b/nfc/java/android/nfc/cardemulation/HostApduService.java deleted file mode 100644 index fbf2203b40b4..000000000000 --- a/nfc/java/android/nfc/cardemulation/HostApduService.java +++ /dev/null @@ -1,446 +0,0 @@ -/* - * Copyright (C) 2015 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.SdkConstant; -import android.annotation.SdkConstant.SdkConstantType; -import android.annotation.SuppressLint; -import android.app.Service; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.nfc.NfcAdapter; -import android.os.Bundle; -import android.os.Handler; -import android.os.IBinder; -import android.os.Message; -import android.os.Messenger; -import android.os.RemoteException; -import android.util.Log; - -import java.util.ArrayList; -import java.util.List; - -/** - * <p>HostApduService is a convenience {@link Service} class that can be - * extended to emulate an NFC card inside an Android - * service component. - * - * <div class="special reference"> - * <h3>Developer Guide</h3> - * For a general introduction to card emulation, see - * <a href="{@docRoot}guide/topics/connectivity/nfc/hce.html"> - * Host-based Card Emulation</a>.</p> - * </div> - * - * <h3>NFC Protocols</h3> - * <p>Cards emulated by this class are based on the NFC-Forum ISO-DEP - * protocol (based on ISO/IEC 14443-4) and support processing - * command Application Protocol Data Units (APDUs) as - * defined in the ISO/IEC 7816-4 specification. - * - * <h3>Service selection</h3> - * <p>When a remote NFC device wants to talk to your - * service, it sends a so-called - * "SELECT AID" APDU as defined in the ISO/IEC 7816-4 specification. - * The AID is an application identifier defined in ISO/IEC 7816-4. - * - * <p>The registration procedure for AIDs is defined in the - * ISO/IEC 7816-5 specification. If you don't want to register an - * AID, you are free to use AIDs in the proprietary range: - * bits 8-5 of the first byte must each be set to '1'. For example, - * "0xF00102030405" is a proprietary AID. If you do use proprietary - * AIDs, it is recommended to choose an AID of at least 6 bytes, - * to reduce the risk of collisions with other applications that - * might be using proprietary AIDs as well. - * - * <h3>AID groups</h3> - * <p>In some cases, a service may need to register multiple AIDs - * to implement a certain application, and it needs to be sure - * that it is the default handler for all of these AIDs (as opposed - * to some AIDs in the group going to another service). - * - * <p>An AID group is a list of AIDs that should be considered as - * belonging together by the OS. For all AIDs in an AID group, the - * OS will guarantee one of the following: - * <ul> - * <li>All AIDs in the group are routed to this service - * <li>No AIDs in the group are routed to this service - * </ul> - * In other words, there is no in-between state, where some AIDs - * in the group can be routed to this service, and some to another. - * <h3>AID groups and categories</h3> - * <p>Each AID group can be associated with a category. This allows - * the Android OS to classify services, and it allows the user to - * set defaults at the category level instead of the AID level. - * - * <p>You can use - * {@link CardEmulation#isDefaultServiceForCategory(android.content.ComponentName, String)} - * to determine if your service is the default handler for a category. - * - * <p>In this version of the platform, the only known categories - * are {@link CardEmulation#CATEGORY_PAYMENT} and {@link CardEmulation#CATEGORY_OTHER}. - * AID groups without a category, or with a category that is not recognized - * by the current platform version, will automatically be - * grouped into the {@link CardEmulation#CATEGORY_OTHER} category. - * <h3>Service AID registration</h3> - * <p>To tell the platform which AIDs groups - * are requested by this service, a {@link #SERVICE_META_DATA} - * entry must be included in the declaration of the service. An - * example of a HostApduService manifest declaration is shown below: - * <pre> <service android:name=".MyHostApduService" android:exported="true" android:permission="android.permission.BIND_NFC_SERVICE"> - * <intent-filter> - * <action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE"/> - * </intent-filter> - * <meta-data android:name="android.nfc.cardemulation.host_apdu_service" android:resource="@xml/apduservice"/> - * </service></pre> - * - * This meta-data tag points to an apduservice.xml file. - * An example of this file with a single AID group declaration is shown below: - * <pre> - * <host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android" - * android:description="@string/servicedesc" android:requireDeviceUnlock="false"> - * <aid-group android:description="@string/aiddescription" android:category="other"> - * <aid-filter android:name="F0010203040506"/> - * <aid-filter android:name="F0394148148100"/> - * </aid-group> - * </host-apdu-service> - * </pre> - * - * <p>The {@link android.R.styleable#HostApduService <host-apdu-service>} is required - * to contain a - * {@link android.R.styleable#HostApduService_description <android:description>} - * attribute that contains a user-friendly description of the service that may be shown in UI. - * The - * {@link android.R.styleable#HostApduService_requireDeviceUnlock <requireDeviceUnlock>} - * attribute can be used to specify that the device must be unlocked before this service - * can be invoked to handle APDUs. - * <p>The {@link android.R.styleable#HostApduService <host-apdu-service>} must - * contain one or more {@link android.R.styleable#AidGroup <aid-group>} tags. - * Each {@link android.R.styleable#AidGroup <aid-group>} must contain one or - * more {@link android.R.styleable#AidFilter <aid-filter>} tags, each of which - * contains a single AID. The AID must be specified in hexadecimal format, and contain - * an even number of characters. - * <h3>AID conflict resolution</h3> - * Multiple HostApduServices may be installed on a single device, and the same AID - * can be registered by more than one service. The Android platform resolves AID - * conflicts depending on which category an AID belongs to. Each category may - * have a different conflict resolution policy. For example, for some categories - * the user may be able to select a default service in the Android settings UI. - * For other categories, to policy may be to always ask the user which service - * is to be invoked in case of conflict. - * - * To query the conflict resolution policy for a certain category, see - * {@link CardEmulation#getSelectionModeForCategory(String)}. - * - * <h3>Data exchange</h3> - * <p>Once the platform has resolved a "SELECT AID" command APDU to a specific - * service component, the "SELECT AID" command APDU and all subsequent - * command APDUs will be sent to that service through - * {@link #processCommandApdu(byte[], Bundle)}, until either: - * <ul> - * <li>The NFC link is broken</li> - * <li>A "SELECT AID" APDU is received which resolves to another service</li> - * </ul> - * These two scenarios are indicated by a call to {@link #onDeactivated(int)}. - * - * <p class="note">Use of this class requires the - * {@link PackageManager#FEATURE_NFC_HOST_CARD_EMULATION} to be present - * on the device. - * - */ -public abstract class HostApduService extends Service { - /** - * The {@link Intent} action that must be declared as handled by the service. - */ - @SdkConstant(SdkConstantType.SERVICE_ACTION) - public static final String SERVICE_INTERFACE = - "android.nfc.cardemulation.action.HOST_APDU_SERVICE"; - - /** - * The name of the meta-data element that contains - * more information about this service. - */ - public static final String SERVICE_META_DATA = - "android.nfc.cardemulation.host_apdu_service"; - - /** - * Reason for {@link #onDeactivated(int)}. - * Indicates deactivation was due to the NFC link - * being lost. - */ - public static final int DEACTIVATION_LINK_LOSS = 0; - - /** - * Reason for {@link #onDeactivated(int)}. - * - * <p>Indicates deactivation was due to a different AID - * being selected (which implicitly deselects the AID - * currently active on the logical channel). - * - * <p>Note that this next AID may still be resolved to this - * service, in which case {@link #processCommandApdu(byte[], Bundle)} - * will be called again. - */ - public static final int DEACTIVATION_DESELECTED = 1; - - static final String TAG = "ApduService"; - - /** - * MSG_COMMAND_APDU is sent by NfcService when - * a 7816-4 command APDU has been received. - * - * @hide - */ - public static final int MSG_COMMAND_APDU = 0; - - /** - * MSG_RESPONSE_APDU is sent to NfcService to send - * a response APDU back to the remote device. - * - * @hide - */ - public static final int MSG_RESPONSE_APDU = 1; - - /** - * MSG_DEACTIVATED is sent by NfcService when - * the current session is finished; either because - * another AID was selected that resolved to - * another service, or because the NFC link - * was deactivated. - * - * @hide - */ - public static final int MSG_DEACTIVATED = 2; - - /** - * - * @hide - */ - public static final int MSG_UNHANDLED = 3; - - /** - * @hide - */ - public static final int MSG_POLLING_LOOP = 4; - - - /** - * @hide - */ - public static final String KEY_DATA = "data"; - - /** - * @hide - */ - public static final String KEY_POLLING_LOOP_FRAMES_BUNDLE = - "android.nfc.cardemulation.POLLING_FRAMES"; - - /** - * Messenger interface to NfcService for sending responses. - * Only accessed on main thread by the message handler. - * - * @hide - */ - Messenger mNfcService = null; - - final Messenger mMessenger = new Messenger(new MsgHandler()); - - final class MsgHandler extends Handler { - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MSG_COMMAND_APDU: - Bundle dataBundle = msg.getData(); - if (dataBundle == null) { - return; - } - if (mNfcService == null) mNfcService = msg.replyTo; - - byte[] apdu = dataBundle.getByteArray(KEY_DATA); - if (apdu != null) { - HostApduService has = HostApduService.this; - byte[] responseApdu = processCommandApdu(apdu, null); - if (responseApdu != null) { - if (mNfcService == null) { - Log.e(TAG, "Response not sent; service was deactivated."); - return; - } - Message responseMsg = Message.obtain(null, MSG_RESPONSE_APDU); - Bundle responseBundle = new Bundle(); - responseBundle.putByteArray(KEY_DATA, responseApdu); - responseMsg.setData(responseBundle); - responseMsg.replyTo = mMessenger; - try { - mNfcService.send(responseMsg); - } catch (RemoteException e) { - Log.e(TAG, "Response not sent; RemoteException calling into " + - "NfcService."); - } - } - } else { - Log.e(TAG, "Received MSG_COMMAND_APDU without data."); - } - break; - case MSG_RESPONSE_APDU: - if (mNfcService == null) { - Log.e(TAG, "Response not sent; service was deactivated."); - return; - } - try { - msg.replyTo = mMessenger; - mNfcService.send(msg); - } catch (RemoteException e) { - Log.e(TAG, "RemoteException calling into NfcService."); - } - break; - case MSG_DEACTIVATED: - // Make sure we won't call into NfcService again - mNfcService = null; - onDeactivated(msg.arg1); - break; - case MSG_UNHANDLED: - if (mNfcService == null) { - Log.e(TAG, "notifyUnhandled not sent; service was deactivated."); - return; - } - try { - msg.replyTo = mMessenger; - mNfcService.send(msg); - } catch (RemoteException e) { - Log.e(TAG, "RemoteException calling into NfcService."); - } - break; - case MSG_POLLING_LOOP: - if (android.nfc.Flags.nfcReadPollingLoop()) { - ArrayList<PollingFrame> pollingFrames = - msg.getData().getParcelableArrayList( - KEY_POLLING_LOOP_FRAMES_BUNDLE, PollingFrame.class); - processPollingFrames(pollingFrames); - } - break; - default: - super.handleMessage(msg); - } - } - } - - @Override - public final IBinder onBind(Intent intent) { - return mMessenger.getBinder(); - } - - /** - * Sends a response APDU back to the remote device. - * - * <p>Note: this method may be called from any thread and will not block. - * @param responseApdu A byte-array containing the reponse APDU. - */ - public final void sendResponseApdu(byte[] responseApdu) { - Message responseMsg = Message.obtain(null, MSG_RESPONSE_APDU); - Bundle dataBundle = new Bundle(); - dataBundle.putByteArray(KEY_DATA, responseApdu); - responseMsg.setData(dataBundle); - try { - mMessenger.send(responseMsg); - } catch (RemoteException e) { - Log.e("TAG", "Local messenger has died."); - } - } - - /** - * Calling this method allows the service to tell the OS - * that it won't be able to complete this transaction - - * for example, because it requires data connectivity - * that is not present at that moment. - * - * The OS may use this indication to give the user a list - * of alternative applications that can handle the last - * AID that was selected. If the user would select an - * application from the list, that action by itself - * will not cause the default to be changed; the selected - * application will be invoked for the next tap only. - * - * If there are no other applications that can handle - * this transaction, the OS will show an error dialog - * indicating your service could not complete the - * transaction. - * - * <p>Note: this method may be called anywhere between - * the first {@link #processCommandApdu(byte[], Bundle)} - * call and a {@link #onDeactivated(int)} call. - */ - public final void notifyUnhandled() { - Message unhandledMsg = Message.obtain(null, MSG_UNHANDLED); - try { - mMessenger.send(unhandledMsg); - } catch (RemoteException e) { - Log.e("TAG", "Local messenger has died."); - } - } - - /** - * This method is called when polling frames have been received from a - * remote device. If the device is in observe mode, the service should - * call {@link NfcAdapter#allowTransaction()} once it is ready to proceed - * with the transaction. If the device is not in observe mode, the service - * can use this polling frame information to determine how to proceed if it - * subsequently has {@link #processCommandApdu(byte[], Bundle)} called. The - * service must override this method inorder to receive polling frames, - * otherwise the base implementation drops the frame. - * - * @param frame A description of the polling frame. - */ - @SuppressLint("OnNameExpected") - @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP) - public void processPollingFrames(@NonNull List<PollingFrame> frame) { - } - - /** - * <p>This method will be called when a command APDU has been received - * from a remote device. A response APDU can be provided directly - * by returning a byte-array in this method. Note that in general - * response APDUs must be sent as quickly as possible, given the fact - * that the user is likely holding their device over an NFC reader - * when this method is called. - * - * <p class="note">If there are multiple services that have registered for the same - * AIDs in their meta-data entry, you will only get called if the user has - * explicitly selected your service, either as a default or just for the next tap. - * - * <p class="note">This method is running on the main thread of your application. - * If you cannot return a response APDU immediately, return null - * and use the {@link #sendResponseApdu(byte[])} method later. - * - * @param commandApdu The APDU that was received from the remote device - * @param extras A bundle containing extra data. May be null. - * @return a byte-array containing the response APDU, or null if no - * response APDU can be sent at this point. - */ - public abstract byte[] processCommandApdu(byte[] commandApdu, Bundle extras); - - /** - * This method will be called in two possible scenarios: - * <li>The NFC link has been deactivated or lost - * <li>A different AID has been selected and was resolved to a different - * service component - * @param reason Either {@link #DEACTIVATION_LINK_LOSS} or {@link #DEACTIVATION_DESELECTED} - */ - public abstract void onDeactivated(int reason); - -} diff --git a/nfc/java/android/nfc/cardemulation/HostNfcFService.java b/nfc/java/android/nfc/cardemulation/HostNfcFService.java deleted file mode 100644 index 65b5ca77de62..000000000000 --- a/nfc/java/android/nfc/cardemulation/HostNfcFService.java +++ /dev/null @@ -1,279 +0,0 @@ -/* - * Copyright (C) 2015 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.SdkConstant; -import android.annotation.SdkConstant.SdkConstantType; -import android.app.Service; -import android.content.ComponentName; -import android.content.Intent; -import android.os.Bundle; -import android.os.Handler; -import android.os.IBinder; -import android.os.Message; -import android.os.Messenger; -import android.os.RemoteException; -import android.util.Log; - -/** - * <p>HostNfcFService is a convenience {@link Service} class that can be - * extended to emulate an NFC-F card inside an Android service component. - * - * <h3>NFC Protocols</h3> - * <p>Cards emulated by this class are based on the NFC-Forum NFC-F - * protocol (based on the JIS-X 6319-4 specification.)</p> - * - * <h3>System Code and NFCID2 registration</h3> - * <p>A {@link HostNfcFService HostNfcFService service} can register - * exactly one System Code and one NFCID2. For details about the use of - * System Code and NFCID2, see the NFC Forum Digital specification.</p> - * <p>To statically register a System Code and NFCID2 with the service, a {@link #SERVICE_META_DATA} - * entry must be included in the declaration of the service. - * - * <p>All {@link HostNfcFService HostNfcFService} declarations in the manifest must require the - * {@link android.Manifest.permission#BIND_NFC_SERVICE} permission - * in their <service> tag, to ensure that only the platform can bind to your service.</p> - * - * <p>An example of a HostNfcFService manifest declaration is shown below: - * - * <pre> <service android:name=".MyHostNfcFService" android:exported="true" android:permission="android.permission.BIND_NFC_SERVICE"> - * <intent-filter> - * <action android:name="android.nfc.cardemulation.action.HOST_NFCF_SERVICE"/> - * </intent-filter> - * <meta-data android:name="android.nfc.cardemulation.host_nfcf_service" android:resource="@xml/nfcfservice"/> - * </service></pre> - * - * This meta-data tag points to an nfcfservice.xml file. - * An example of this file with a System Code and NFCID2 declaration is shown below: - * <pre> - * <host-nfcf-service xmlns:android="http://schemas.android.com/apk/res/android" - * android:description="@string/servicedesc"> - * <system-code-filter android:name="4000"/> - * <nfcid2-filter android:name="02FE000000000000"/> - <t3tPmm-filter android:name="FFFFFFFFFFFFFFFF"/> - * </host-nfcf-service> - * </pre> - * - * <p>The {@link android.R.styleable#HostNfcFService <host-nfcf-service>} is required - * to contain a - * {@link android.R.styleable#HostApduService_description <android:description>} - * attribute that contains a user-friendly description of the service that may be shown in UI. - * <p>The {@link android.R.styleable#HostNfcFService <host-nfcf-service>} must - * contain: - * <ul> - * <li>Exactly one {@link android.R.styleable#SystemCodeFilter <system-code-filter>} tag.</li> - * <li>Exactly one {@link android.R.styleable#Nfcid2Filter <nfcid2-filter>} tag.</li> - * <li>Zero or one {@link android.R.styleable#T3tPmmFilter <t3tPmm-filter>} tag.</li> - * </ul> - * </p> - * - * <p>Alternatively, the System Code and NFCID2 can be dynamically registererd for a service - * by using the {@link NfcFCardEmulation#registerSystemCodeForService(ComponentName, String)} and - * {@link NfcFCardEmulation#setNfcid2ForService(ComponentName, String)} methods. - * </p> - * - * <h3>Service selection</h3> - * <p>When a remote NFC devices wants to communicate with your service, it - * sends a SENSF_REQ command to the NFC controller, requesting a System Code. - * If a {@link NfcFCardEmulation NfcFCardEmulation service} has registered - * this system code and has been enabled by the foreground application, the - * NFC controller will respond with the NFCID2 that is registered for this service. - * The reader can then continue data exchange with this service by using the NFCID2.</p> - * - * <h3>Data exchange</h3> - * <p>After service selection, all frames addressed to the NFCID2 of this service will - * be sent through {@link #processNfcFPacket(byte[], Bundle)}, until the NFC link is - * broken.<p> - * - * <p>When the NFC link is broken, {@link #onDeactivated(int)} will be called.</p> - */ -public abstract class HostNfcFService extends Service { - /** - * The {@link Intent} action that must be declared as handled by the service. - */ - @SdkConstant(SdkConstantType.SERVICE_ACTION) - public static final String SERVICE_INTERFACE = - "android.nfc.cardemulation.action.HOST_NFCF_SERVICE"; - - /** - * The name of the meta-data element that contains - * more information about this service. - */ - public static final String SERVICE_META_DATA = - "android.nfc.cardemulation.host_nfcf_service"; - - /** - * Reason for {@link #onDeactivated(int)}. - * Indicates deactivation was due to the NFC link - * being lost. - */ - public static final int DEACTIVATION_LINK_LOSS = 0; - - static final String TAG = "NfcFService"; - - /** - * MSG_COMMAND_PACKET is sent by NfcService when - * a NFC-F command packet has been received. - * - * @hide - */ - public static final int MSG_COMMAND_PACKET = 0; - - /** - * MSG_RESPONSE_PACKET is sent to NfcService to send - * a response packet back to the remote device. - * - * @hide - */ - public static final int MSG_RESPONSE_PACKET = 1; - - /** - * MSG_DEACTIVATED is sent by NfcService when - * the current session is finished; because - * the NFC link was deactivated. - * - * @hide - */ - public static final int MSG_DEACTIVATED = 2; - - /** - * @hide - */ - public static final String KEY_DATA = "data"; - - /** - * @hide - */ - public static final String KEY_MESSENGER = "messenger"; - - /** - * Messenger interface to NfcService for sending responses. - * Only accessed on main thread by the message handler. - * - * @hide - */ - Messenger mNfcService = null; - - final Messenger mMessenger = new Messenger(new MsgHandler()); - - final class MsgHandler extends Handler { - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MSG_COMMAND_PACKET: - Bundle dataBundle = msg.getData(); - if (dataBundle == null) { - return; - } - if (mNfcService == null) mNfcService = msg.replyTo; - - byte[] packet = dataBundle.getByteArray(KEY_DATA); - if (packet != null) { - byte[] responsePacket = processNfcFPacket(packet, null); - if (responsePacket != null) { - if (mNfcService == null) { - Log.e(TAG, "Response not sent; service was deactivated."); - return; - } - Message responseMsg = Message.obtain(null, MSG_RESPONSE_PACKET); - Bundle responseBundle = new Bundle(); - responseBundle.putByteArray(KEY_DATA, responsePacket); - responseMsg.setData(responseBundle); - responseMsg.replyTo = mMessenger; - try { - mNfcService.send(responseMsg); - } catch (RemoteException e) { - Log.e("TAG", "Response not sent; RemoteException calling into " + - "NfcService."); - } - } - } else { - Log.e(TAG, "Received MSG_COMMAND_PACKET without data."); - } - break; - case MSG_RESPONSE_PACKET: - if (mNfcService == null) { - Log.e(TAG, "Response not sent; service was deactivated."); - return; - } - try { - msg.replyTo = mMessenger; - mNfcService.send(msg); - } catch (RemoteException e) { - Log.e(TAG, "RemoteException calling into NfcService."); - } - break; - case MSG_DEACTIVATED: - // Make sure we won't call into NfcService again - mNfcService = null; - onDeactivated(msg.arg1); - break; - default: - super.handleMessage(msg); - } - } - } - - @Override - public final IBinder onBind(Intent intent) { - return mMessenger.getBinder(); - } - - /** - * Sends a response packet back to the remote device. - * - * <p>Note: this method may be called from any thread and will not block. - * @param responsePacket A byte-array containing the response packet. - */ - public final void sendResponsePacket(byte[] responsePacket) { - Message responseMsg = Message.obtain(null, MSG_RESPONSE_PACKET); - Bundle dataBundle = new Bundle(); - dataBundle.putByteArray(KEY_DATA, responsePacket); - responseMsg.setData(dataBundle); - try { - mMessenger.send(responseMsg); - } catch (RemoteException e) { - Log.e("TAG", "Local messenger has died."); - } - } - - /** - * <p>This method will be called when a NFC-F packet has been received - * from a remote device. A response packet can be provided directly - * by returning a byte-array in this method. Note that in general - * response packets must be sent as quickly as possible, given the fact - * that the user is likely holding their device over an NFC reader - * when this method is called. - * - * <p class="note">This method is running on the main thread of your application. - * If you cannot return a response packet immediately, return null - * and use the {@link #sendResponsePacket(byte[])} method later. - * - * @param commandPacket The NFC-F packet that was received from the remote device - * @param extras A bundle containing extra data. May be null. - * @return a byte-array containing the response packet, or null if no - * response packet can be sent at this point. - */ - public abstract byte[] processNfcFPacket(byte[] commandPacket, Bundle extras); - - /** - * This method will be called in following possible scenarios: - * <li>The NFC link has been lost - * @param reason {@link #DEACTIVATION_LINK_LOSS} - */ - public abstract void onDeactivated(int reason); -} diff --git a/nfc/java/android/nfc/cardemulation/NfcFCardEmulation.java b/nfc/java/android/nfc/cardemulation/NfcFCardEmulation.java deleted file mode 100644 index 48bbf5b61052..000000000000 --- a/nfc/java/android/nfc/cardemulation/NfcFCardEmulation.java +++ /dev/null @@ -1,473 +0,0 @@ -/* - * Copyright (C) 2015 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.app.Activity; -import android.content.ComponentName; -import android.content.Context; -import android.content.pm.PackageManager; -import android.nfc.INfcFCardEmulation; -import android.nfc.NfcAdapter; -import android.os.RemoteException; -import android.util.Log; - -import java.util.HashMap; -import java.util.List; - -/** - * This class can be used to query the state of - * NFC-F card emulation services. - * - * For a general introduction into NFC card emulation, - * please read the <a href="{@docRoot}guide/topics/connectivity/nfc/hce.html"> - * NFC card emulation developer guide</a>.</p> - * - * <p class="note">Use of this class requires the - * {@link PackageManager#FEATURE_NFC_HOST_CARD_EMULATION_NFCF} - * to be present on the device. - */ -public final class NfcFCardEmulation { - static final String TAG = "NfcFCardEmulation"; - - static boolean sIsInitialized = false; - static HashMap<Context, NfcFCardEmulation> sCardEmus = new HashMap<Context, NfcFCardEmulation>(); - static INfcFCardEmulation sService; - - final Context mContext; - - private NfcFCardEmulation(Context context, INfcFCardEmulation service) { - mContext = context.getApplicationContext(); - sService = service; - } - - /** - * Helper to get an instance of this class. - * - * @param adapter A reference to an NfcAdapter object. - * @return - */ - public static synchronized NfcFCardEmulation getInstance(NfcAdapter adapter) { - if (adapter == null) throw new NullPointerException("NfcAdapter is null"); - Context context = adapter.getContext(); - if (context == null) { - Log.e(TAG, "NfcAdapter context is null."); - throw new UnsupportedOperationException(); - } - if (!sIsInitialized) { - PackageManager pm = context.getPackageManager(); - if (pm == null) { - Log.e(TAG, "Cannot get PackageManager"); - throw new UnsupportedOperationException(); - } - if (!pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION_NFCF)) { - Log.e(TAG, "This device does not support NFC-F card emulation"); - throw new UnsupportedOperationException(); - } - sIsInitialized = true; - } - NfcFCardEmulation manager = sCardEmus.get(context); - if (manager == null) { - // Get card emu service - INfcFCardEmulation service = adapter.getNfcFCardEmulationService(); - if (service == null) { - Log.e(TAG, "This device does not implement the INfcFCardEmulation interface."); - throw new UnsupportedOperationException(); - } - manager = new NfcFCardEmulation(context, service); - sCardEmus.put(context, manager); - } - return manager; - } - - /** - * Retrieves the current System Code for the specified service. - * - * <p>Before calling {@link #registerSystemCodeForService(ComponentName, String)}, - * the System Code contained in the Manifest file is returned. After calling - * {@link #registerSystemCodeForService(ComponentName, String)}, the System Code - * registered there is returned. After calling - * {@link #unregisterSystemCodeForService(ComponentName)}, "null" is returned. - * - * @param service The component name of the service - * @return the current System Code - */ - public String getSystemCodeForService(ComponentName service) throws RuntimeException { - if (service == null) { - throw new NullPointerException("service is null"); - } - try { - return sService.getSystemCodeForService(mContext.getUser().getIdentifier(), service); - } catch (RemoteException e) { - // Try one more time - recoverService(); - if (sService == null) { - Log.e(TAG, "Failed to recover CardEmulationService."); - return null; - } - try { - return sService.getSystemCodeForService(mContext.getUser().getIdentifier(), - service); - } catch (RemoteException ee) { - Log.e(TAG, "Failed to reach CardEmulationService."); - ee.rethrowAsRuntimeException(); - return null; - } - } - } - - /** - * Registers a System Code for the specified service. - * - * <p>The System Code must be in range from "4000" to "4FFF" (excluding "4*FF"). - * - * <p>If a System Code was previously registered for this service - * (either statically through the manifest, or dynamically by using this API), - * it will be replaced with this one. - * - * <p>Even if the same System Code is already registered for another service, - * this method succeeds in registering the System Code. - * - * <p>Note that you can only register a System Code for a service that - * is running under the same UID as the caller of this API. Typically - * this means you need to call this from the same - * package as the service itself, though UIDs can also - * be shared between packages using shared UIDs. - * - * @param service The component name of the service - * @param systemCode The System Code to be registered - * @return whether the registration was successful. - */ - public boolean registerSystemCodeForService(ComponentName service, String systemCode) - throws RuntimeException { - if (service == null || systemCode == null) { - throw new NullPointerException("service or systemCode is null"); - } - try { - return sService.registerSystemCodeForService(mContext.getUser().getIdentifier(), - service, systemCode); - } catch (RemoteException e) { - // Try one more time - recoverService(); - if (sService == null) { - Log.e(TAG, "Failed to recover CardEmulationService."); - return false; - } - try { - return sService.registerSystemCodeForService(mContext.getUser().getIdentifier(), - service, systemCode); - } catch (RemoteException ee) { - Log.e(TAG, "Failed to reach CardEmulationService."); - ee.rethrowAsRuntimeException(); - return false; - } - } - } - - /** - * Removes a registered System Code for the specified service. - * - * @param service The component name of the service - * @return whether the System Code was successfully removed. - */ - public boolean unregisterSystemCodeForService(ComponentName service) throws RuntimeException { - if (service == null) { - throw new NullPointerException("service is null"); - } - try { - return sService.removeSystemCodeForService(mContext.getUser().getIdentifier(), service); - } catch (RemoteException e) { - // Try one more time - recoverService(); - if (sService == null) { - Log.e(TAG, "Failed to recover CardEmulationService."); - return false; - } - try { - return sService.removeSystemCodeForService(mContext.getUser().getIdentifier(), - service); - } catch (RemoteException ee) { - Log.e(TAG, "Failed to reach CardEmulationService."); - ee.rethrowAsRuntimeException(); - return false; - } - } - } - - /** - * Retrieves the current NFCID2 for the specified service. - * - * <p>Before calling {@link #setNfcid2ForService(ComponentName, String)}, - * the NFCID2 contained in the Manifest file is returned. If "random" is specified - * in the Manifest file, a random number assigned by the system at installation time - * is returned. After setting an NFCID2 - * with {@link #setNfcid2ForService(ComponentName, String)}, this NFCID2 is returned. - * - * @param service The component name of the service - * @return the current NFCID2 - */ - public String getNfcid2ForService(ComponentName service) throws RuntimeException { - if (service == null) { - throw new NullPointerException("service is null"); - } - try { - return sService.getNfcid2ForService(mContext.getUser().getIdentifier(), service); - } catch (RemoteException e) { - // Try one more time - recoverService(); - if (sService == null) { - Log.e(TAG, "Failed to recover CardEmulationService."); - return null; - } - try { - return sService.getNfcid2ForService(mContext.getUser().getIdentifier(), service); - } catch (RemoteException ee) { - Log.e(TAG, "Failed to reach CardEmulationService."); - ee.rethrowAsRuntimeException(); - return null; - } - } - } - - /** - * Set a NFCID2 for the specified service. - * - * <p>The NFCID2 must be in range from "02FE000000000000" to "02FEFFFFFFFFFFFF". - * - * <p>If a NFCID2 was previously set for this service - * (either statically through the manifest, or dynamically by using this API), - * it will be replaced. - * - * <p>Note that you can only set the NFCID2 for a service that - * is running under the same UID as the caller of this API. Typically - * this means you need to call this from the same - * package as the service itself, though UIDs can also - * be shared between packages using shared UIDs. - * - * @param service The component name of the service - * @param nfcid2 The NFCID2 to be registered - * @return whether the setting was successful. - */ - public boolean setNfcid2ForService(ComponentName service, String nfcid2) - throws RuntimeException { - if (service == null || nfcid2 == null) { - throw new NullPointerException("service or nfcid2 is null"); - } - try { - return sService.setNfcid2ForService(mContext.getUser().getIdentifier(), - service, nfcid2); - } catch (RemoteException e) { - // Try one more time - recoverService(); - if (sService == null) { - Log.e(TAG, "Failed to recover CardEmulationService."); - return false; - } - try { - return sService.setNfcid2ForService(mContext.getUser().getIdentifier(), - service, nfcid2); - } catch (RemoteException ee) { - Log.e(TAG, "Failed to reach CardEmulationService."); - ee.rethrowAsRuntimeException(); - return false; - } - } - } - - /** - * Allows a foreground application to specify which card emulation service - * should be enabled while a specific Activity is in the foreground. - * - * <p>The specified HCE-F service is only enabled when the corresponding application is - * in the foreground and this method has been called. When the application is moved to - * the background, {@link #disableService(Activity)} is called, or - * NFCID2 or System Code is replaced, the HCE-F service is disabled. - * - * <p>The specified Activity must currently be in resumed state. A good - * paradigm is to call this method in your {@link Activity#onResume}, and to call - * {@link #disableService(Activity)} in your {@link Activity#onPause}. - * - * <p>Note that this preference is not persisted by the OS, and hence must be - * called every time the Activity is resumed. - * - * @param activity The activity which prefers this service to be invoked - * @param service The service to be preferred while this activity is in the foreground - * @return whether the registration was successful - */ - public boolean enableService(Activity activity, ComponentName service) throws RuntimeException { - if (activity == null || service == null) { - throw new NullPointerException("activity or service is null"); - } - // Verify the activity is in the foreground before calling into NfcService - if (!activity.isResumed()) { - throw new IllegalArgumentException("Activity must be resumed."); - } - try { - return sService.enableNfcFForegroundService(service); - } catch (RemoteException e) { - // Try one more time - recoverService(); - if (sService == null) { - Log.e(TAG, "Failed to recover CardEmulationService."); - return false; - } - try { - return sService.enableNfcFForegroundService(service); - } catch (RemoteException ee) { - Log.e(TAG, "Failed to reach CardEmulationService."); - ee.rethrowAsRuntimeException(); - return false; - } - } - } - - /** - * Disables the service for the specified Activity. - * - * <p>Note that the specified Activity must still be in resumed - * state at the time of this call. A good place to call this method - * is in your {@link Activity#onPause} implementation. - * - * @param activity The activity which the service was registered for - * @return true when successful - */ - public boolean disableService(Activity activity) throws RuntimeException { - if (activity == null) { - throw new NullPointerException("activity is null"); - } - if (!activity.isResumed()) { - throw new IllegalArgumentException("Activity must be resumed."); - } - try { - return sService.disableNfcFForegroundService(); - } catch (RemoteException e) { - // Try one more time - recoverService(); - if (sService == null) { - Log.e(TAG, "Failed to recover CardEmulationService."); - return false; - } - try { - return sService.disableNfcFForegroundService(); - } catch (RemoteException ee) { - Log.e(TAG, "Failed to reach CardEmulationService."); - ee.rethrowAsRuntimeException(); - return false; - } - } - } - - /** - * @hide - */ - public List<NfcFServiceInfo> getNfcFServices() { - try { - return sService.getNfcFServices(mContext.getUser().getIdentifier()); - } catch (RemoteException e) { - // Try one more time - recoverService(); - if (sService == null) { - Log.e(TAG, "Failed to recover CardEmulationService."); - return null; - } - try { - return sService.getNfcFServices(mContext.getUser().getIdentifier()); - } catch (RemoteException ee) { - Log.e(TAG, "Failed to reach CardEmulationService."); - return null; - } - } - } - - /** - * @hide - */ - public int getMaxNumOfRegisterableSystemCodes() { - try { - return sService.getMaxNumOfRegisterableSystemCodes(); - } catch (RemoteException e) { - // Try one more time - recoverService(); - if (sService == null) { - Log.e(TAG, "Failed to recover CardEmulationService."); - return -1; - } - try { - return sService.getMaxNumOfRegisterableSystemCodes(); - } catch (RemoteException ee) { - Log.e(TAG, "Failed to reach CardEmulationService."); - return -1; - } - } - } - - /** - * @hide - */ - public static boolean isValidSystemCode(String systemCode) { - if (systemCode == null) { - return false; - } - if (systemCode.length() != 4) { - Log.e(TAG, "System Code " + systemCode + " is not a valid System Code."); - return false; - } - // check if the value is between "4000" and "4FFF" (excluding "4*FF") - if (!systemCode.startsWith("4") || systemCode.toUpperCase().endsWith("FF")) { - Log.e(TAG, "System Code " + systemCode + " is not a valid System Code."); - return false; - } - try { - Integer.parseInt(systemCode, 16); - } catch (NumberFormatException e) { - Log.e(TAG, "System Code " + systemCode + " is not a valid System Code."); - return false; - } - return true; - } - - /** - * @hide - */ - public static boolean isValidNfcid2(String nfcid2) { - if (nfcid2 == null) { - return false; - } - if (nfcid2.length() != 16) { - Log.e(TAG, "NFCID2 " + nfcid2 + " is not a valid NFCID2."); - return false; - } - // check if the the value starts with "02FE" - if (!nfcid2.toUpperCase().startsWith("02FE")) { - Log.e(TAG, "NFCID2 " + nfcid2 + " is not a valid NFCID2."); - return false; - } - try { - Long.parseLong(nfcid2, 16); - } catch (NumberFormatException e) { - Log.e(TAG, "NFCID2 " + nfcid2 + " is not a valid NFCID2."); - return false; - } - return true; - } - - void recoverService() { - NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext); - sService = adapter.getNfcFCardEmulationService(); - } - -} - diff --git a/nfc/java/android/nfc/cardemulation/OffHostApduService.java b/nfc/java/android/nfc/cardemulation/OffHostApduService.java deleted file mode 100644 index 8d8a17270523..000000000000 --- a/nfc/java/android/nfc/cardemulation/OffHostApduService.java +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright (C) 2015 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.SdkConstant; -import android.annotation.SdkConstant.SdkConstantType; -import android.app.Service; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.os.IBinder; - -/** - * <p>OffHostApduService is a convenience {@link Service} class that can be - * extended to describe one or more NFC applications that are residing - * off-host, for example on an embedded secure element or a UICC. - * - * <div class="special reference"> - * <h3>Developer Guide</h3> - * For a general introduction into the topic of card emulation, - * please read the <a href="{@docRoot}guide/topics/connectivity/nfc/hce.html"> - * NFC card emulation developer guide.</a></p> - * </div> - * - * <h3>NFC Protocols</h3> - * <p>Off-host applications represented by this class are based on the NFC-Forum ISO-DEP - * protocol (based on ISO/IEC 14443-4) and support processing - * command Application Protocol Data Units (APDUs) as - * defined in the ISO/IEC 7816-4 specification. - * - * <h3>Service selection</h3> - * <p>When a remote NFC device wants to talk to your - * off-host NFC application, it sends a so-called - * "SELECT AID" APDU as defined in the ISO/IEC 7816-4 specification. - * The AID is an application identifier defined in ISO/IEC 7816-4. - * - * <p>The registration procedure for AIDs is defined in the - * ISO/IEC 7816-5 specification. If you don't want to register an - * AID, you are free to use AIDs in the proprietary range: - * bits 8-5 of the first byte must each be set to '1'. For example, - * "0xF00102030405" is a proprietary AID. If you do use proprietary - * AIDs, it is recommended to choose an AID of at least 6 bytes, - * to reduce the risk of collisions with other applications that - * might be using proprietary AIDs as well. - * - * <h3>AID groups</h3> - * <p>In some cases, an off-host environment may need to register multiple AIDs - * to implement a certain application, and it needs to be sure - * that it is the default handler for all of these AIDs (as opposed - * to some AIDs in the group going to another service). - * - * <p>An AID group is a list of AIDs that should be considered as - * belonging together by the OS. For all AIDs in an AID group, the - * OS will guarantee one of the following: - * <ul> - * <li>All AIDs in the group are routed to the off-host execution environment - * <li>No AIDs in the group are routed to the off-host execution environment - * </ul> - * In other words, there is no in-between state, where some AIDs - * in the group can be routed to this off-host execution environment, - * and some to another or a host-based {@link HostApduService}. - * <h3>AID groups and categories</h3> - * <p>Each AID group can be associated with a category. This allows - * the Android OS to classify services, and it allows the user to - * set defaults at the category level instead of the AID level. - * - * <p>You can use - * {@link CardEmulation#isDefaultServiceForCategory(android.content.ComponentName, String)} - * to determine if your off-host service is the default handler for a category. - * - * <p>In this version of the platform, the only known categories - * are {@link CardEmulation#CATEGORY_PAYMENT} and {@link CardEmulation#CATEGORY_OTHER}. - * AID groups without a category, or with a category that is not recognized - * by the current platform version, will automatically be - * grouped into the {@link CardEmulation#CATEGORY_OTHER} category. - * - * <h3>Service AID registration</h3> - * <p>To tell the platform which AIDs - * reside off-host and are managed by this service, a {@link #SERVICE_META_DATA} - * entry must be included in the declaration of the service. An - * example of a OffHostApduService manifest declaration is shown below: - * <pre> <service android:name=".MyOffHostApduService" android:exported="true" android:permission="android.permission.BIND_NFC_SERVICE"> - * <intent-filter> - * <action android:name="android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE"/> - * </intent-filter> - * <meta-data android:name="android.nfc.cardemulation.off_host_apdu_service" android:resource="@xml/apduservice"/> - * </service></pre> - * - * This meta-data tag points to an apduservice.xml file. - * An example of this file with a single AID group declaration is shown below: - * <pre> - * <offhost-apdu-service xmlns:android="http://schemas.android.com/apk/res/android" - * android:description="@string/servicedesc"> - * <aid-group android:description="@string/subscription" android:category="other"> - * <aid-filter android:name="F0010203040506"/> - * <aid-filter android:name="F0394148148100"/> - * </aid-group> - * </offhost-apdu-service> - * </pre> - * - * <p>The {@link android.R.styleable#OffHostApduService <offhost-apdu-service>} is required - * to contain a - * {@link android.R.styleable#OffHostApduService_description <android:description>} - * attribute that contains a user-friendly description of the service that may be shown in UI. - * - * <p>The {@link android.R.styleable#OffHostApduService <offhost-apdu-service>} must - * contain one or more {@link android.R.styleable#AidGroup <aid-group>} tags. - * Each {@link android.R.styleable#AidGroup <aid-group>} must contain one or - * more {@link android.R.styleable#AidFilter <aid-filter>} tags, each of which - * contains a single AID. The AID must be specified in hexadecimal format, and contain - * an even number of characters. - * - * <p>This registration will allow the service to be included - * as an option for being the default handler for categories. - * The Android OS will take care of correctly - * routing the AIDs to the off-host execution environment, - * based on which service the user has selected to be the handler for a certain category. - * - * <p>The service may define additional actions outside of the - * Android namespace that provide further interaction with - * the off-host execution environment. - * - * <p class="note">Use of this class requires the - * {@link PackageManager#FEATURE_NFC_HOST_CARD_EMULATION} to be present - * on the device. - */ -public abstract class OffHostApduService extends Service { - /** - * The {@link Intent} action that must be declared as handled by the service. - */ - @SdkConstant(SdkConstantType.SERVICE_ACTION) - public static final String SERVICE_INTERFACE = - "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE"; - - /** - * The name of the meta-data element that contains - * more information about this service. - */ - public static final String SERVICE_META_DATA = - "android.nfc.cardemulation.off_host_apdu_service"; - - /** - * The Android platform itself will not bind to this service, - * but merely uses its declaration to keep track of what AIDs - * the service is interested in. This information is then used - * to present the user with a list of applications that can handle - * an AID, as well as correctly route those AIDs either to the host (in case - * the user preferred a {@link HostApduService}), or to an off-host - * execution environment (in case the user preferred a {@link OffHostApduService}. - * - * Implementers may define additional actions outside of the - * Android namespace that allow further interactions with - * the off-host execution environment. Such implementations - * would need to override this method. - */ - public abstract IBinder onBind(Intent intent); -} diff --git a/nfc/java/android/nfc/cardemulation/PollingFrame.aidl b/nfc/java/android/nfc/cardemulation/PollingFrame.aidl deleted file mode 100644 index 8e09f8baaff2..000000000000 --- a/nfc/java/android/nfc/cardemulation/PollingFrame.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 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.cardemulation; - -parcelable PollingFrame;
\ No newline at end of file diff --git a/nfc/java/android/nfc/cardemulation/PollingFrame.java b/nfc/java/android/nfc/cardemulation/PollingFrame.java deleted file mode 100644 index 5dcc84ccf8b9..000000000000 --- a/nfc/java/android/nfc/cardemulation/PollingFrame.java +++ /dev/null @@ -1,283 +0,0 @@ -/* - * 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.IntDef; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.content.ComponentName; -import android.os.Bundle; -import android.os.Parcel; -import android.os.Parcelable; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.HexFormat; -import java.util.List; - -/** - * Polling Frames represent data about individual frames of an NFC polling loop. These frames will - * be delivered to subclasses of {@link HostApduService} that have registered filters with - * {@link CardEmulation#registerPollingLoopFilterForService(ComponentName, String, boolean)} 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 { - - /** - * @hide - */ - @IntDef(prefix = { "POLLING_LOOP_TYPE_"}, - value = { - POLLING_LOOP_TYPE_A, - POLLING_LOOP_TYPE_B, - POLLING_LOOP_TYPE_F, - POLLING_LOOP_TYPE_OFF, - POLLING_LOOP_TYPE_ON, - POLLING_LOOP_TYPE_UNKNOWN - }) - @Retention(RetentionPolicy.SOURCE) - @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP) - public @interface PollingFrameType {} - - /** - * POLLING_LOOP_TYPE_A is the value associated with the key - * POLLING_LOOP_TYPE in the Bundle passed to {@link HostApduService#processPollingFrames(List)} - * when the polling loop is for NFC-A. - */ - @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP) - public static final int POLLING_LOOP_TYPE_A = 'A'; - - /** - * POLLING_LOOP_TYPE_B is the value associated with the key - * POLLING_LOOP_TYPE in the Bundle passed to {@link HostApduService#processPollingFrames(List)} - * when the polling loop is for NFC-B. - */ - @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP) - public static final int POLLING_LOOP_TYPE_B = 'B'; - - /** - * POLLING_LOOP_TYPE_F is the value associated with the key - * POLLING_LOOP_TYPE in the Bundle passed to {@link HostApduService#processPollingFrames(List)} - * when the polling loop is for NFC-F. - */ - @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP) - public static final int POLLING_LOOP_TYPE_F = 'F'; - - /** - * POLLING_LOOP_TYPE_ON is the value associated with the key - * POLLING_LOOP_TYPE in the Bundle passed to {@link HostApduService#processPollingFrames(List)} - * when the polling loop turns on. - */ - @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP) - public static final int POLLING_LOOP_TYPE_ON = 'O'; - - /** - * POLLING_LOOP_TYPE_OFF is the value associated with the key - * POLLING_LOOP_TYPE in the Bundle passed to {@link HostApduService#processPollingFrames(List)} - * when the polling loop turns off. - */ - @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP) - public static final int POLLING_LOOP_TYPE_OFF = 'X'; - - /** - * POLLING_LOOP_TYPE_UNKNOWN is the value associated with the key - * POLLING_LOOP_TYPE in the Bundle passed to {@link HostApduService#processPollingFrames(List)} - * when the polling loop frame isn't recognized. - */ - @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP) - public static final int POLLING_LOOP_TYPE_UNKNOWN = 'U'; - - /** - * KEY_POLLING_LOOP_TYPE is the Bundle key for the type of - * polling loop frame in the Bundle included in MSG_POLLING_LOOP. - */ - @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP) - private static final String KEY_POLLING_LOOP_TYPE = "android.nfc.cardemulation.TYPE"; - - /** - * KEY_POLLING_LOOP_DATA is the Bundle key for the raw data of captured from - * the polling loop frame in the Bundle included in MSG_POLLING_LOOP. - */ - @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP) - private 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 included in MSG_POLLING_LOOP. - */ - @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP) - private 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 included in MSG_POLLING_LOOP. - */ - @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP) - private static final String KEY_POLLING_LOOP_TIMESTAMP = "android.nfc.cardemulation.TIMESTAMP"; - - /** - * KEY_POLLING_LOOP_TIMESTAMP is the Bundle key for whether this polling frame triggered - * autoTransact in the Bundle included in MSG_POLLING_LOOP. - */ - @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP) - private static final String KEY_POLLING_LOOP_TRIGGERED_AUTOTRANSACT = - "android.nfc.cardemulation.TRIGGERED_AUTOTRANSACT"; - - - @PollingFrameType - private final int mType; - private final byte[] mData; - private final int mGain; - private final long mTimestamp; - private boolean mTriggeredAutoTransact; - - public static final @NonNull Parcelable.Creator<PollingFrame> CREATOR = - new Parcelable.Creator<>() { - @Override - public PollingFrame createFromParcel(Parcel source) { - return new PollingFrame(source.readBundle()); - } - - @Override - public PollingFrame[] newArray(int size) { - return new PollingFrame[size]; - } - }; - - private PollingFrame(Bundle frame) { - mType = frame.getInt(KEY_POLLING_LOOP_TYPE); - byte[] data = frame.getByteArray(KEY_POLLING_LOOP_DATA); - mData = (data == null) ? new byte[0] : data; - mGain = frame.getInt(KEY_POLLING_LOOP_GAIN, -1); - mTimestamp = frame.getLong(KEY_POLLING_LOOP_TIMESTAMP); - mTriggeredAutoTransact = frame.containsKey(KEY_POLLING_LOOP_TRIGGERED_AUTOTRANSACT) - && frame.getBoolean(KEY_POLLING_LOOP_TRIGGERED_AUTOTRANSACT); - } - - /** - * Constructor for Polling Frames. - * - * @param type the type of the frame - * @param data a byte array of the data contained in the frame - * @param gain the vendor-specific gain of the field - * @param timestampMicros the timestamp in microseconds - * @param triggeredAutoTransact whether or not this frame triggered the device to start a - * transaction automatically - * - * @hide - */ - public PollingFrame(@PollingFrameType int type, @Nullable byte[] data, - int gain, long timestampMicros, boolean triggeredAutoTransact) { - mType = type; - mData = data == null ? new byte[0] : data; - mGain = gain; - mTimestamp = timestampMicros; - mTriggeredAutoTransact = triggeredAutoTransact; - } - - /** - * Returns the type of frame for this polling loop frame. - * The possible return values are: - * <ul> - * <li>{@link #POLLING_LOOP_TYPE_ON}</li> - * <li>{@link #POLLING_LOOP_TYPE_OFF}</li> - * <li>{@link #POLLING_LOOP_TYPE_A}</li> - * <li>{@link #POLLING_LOOP_TYPE_B}</li> - * <li>{@link #POLLING_LOOP_TYPE_F}</li> - * </ul> - */ - public @PollingFrameType int 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. - * @return the gain or -1 if there is no gain measurement associated with this frame. - */ - public int getVendorSpecificGain() { - return mGain; - } - - /** - * Returns the timestamp of when the polling loop frame was observed, in microseconds. These - * timestamps are relative and should only be used for comparing the timing of frames relative - * to each other. - * @return the timestamp in microseconds - */ - public long getTimestamp() { - return mTimestamp; - } - - /** - * @hide - */ - public void setTriggeredAutoTransact(boolean triggeredAutoTransact) { - mTriggeredAutoTransact = triggeredAutoTransact; - } - - /** - * Returns whether this frame triggered the device to automatically disable observe mode and - * allow one transaction. - */ - public boolean getTriggeredAutoTransact() { - return mTriggeredAutoTransact; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeBundle(toBundle()); - } - - /** - * @return a Bundle representing this frame - */ - private Bundle toBundle() { - Bundle frame = new Bundle(); - frame.putInt(KEY_POLLING_LOOP_TYPE, getType()); - if (getVendorSpecificGain() != -1) { - frame.putInt(KEY_POLLING_LOOP_GAIN, (byte) getVendorSpecificGain()); - } - frame.putByteArray(KEY_POLLING_LOOP_DATA, getData()); - frame.putLong(KEY_POLLING_LOOP_TIMESTAMP, getTimestamp()); - frame.putBoolean(KEY_POLLING_LOOP_TRIGGERED_AUTOTRANSACT, getTriggeredAutoTransact()); - return frame; - } - - @Override - public String toString() { - return "PollingFrame { Type: " + (char) getType() - + ", gain: " + getVendorSpecificGain() - + ", timestamp: " + Long.toUnsignedString(getTimestamp()) - + ", data: [" + HexFormat.ofDelimiter(" ").formatHex(getData()) + "] }"; - } -} diff --git a/nfc/java/android/nfc/cardemulation/Utils.java b/nfc/java/android/nfc/cardemulation/Utils.java deleted file mode 100644 index 202e1cfb48f6..000000000000 --- a/nfc/java/android/nfc/cardemulation/Utils.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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.NonNull; -import android.content.ComponentName; -import android.content.ComponentNameProto; -import android.util.proto.ProtoOutputStream; - -/** @hide */ -public final class Utils { - private Utils() { - } - - /** Copied from {@link ComponentName#dumpDebug(ProtoOutputStream, long)} */ - public static void dumpDebugComponentName( - @NonNull ComponentName componentName, @NonNull ProtoOutputStream proto, long fieldId) { - final long token = proto.start(fieldId); - proto.write(ComponentNameProto.PACKAGE_NAME, componentName.getPackageName()); - proto.write(ComponentNameProto.CLASS_NAME, componentName.getClassName()); - proto.end(token); - } -} diff --git a/nfc/java/android/nfc/dta/NfcDta.java b/nfc/java/android/nfc/dta/NfcDta.java deleted file mode 100644 index 88016623434d..000000000000 --- a/nfc/java/android/nfc/dta/NfcDta.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright (C) 2017 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.dta; - -import android.content.Context; -import android.nfc.INfcDta; -import android.nfc.NfcAdapter; -import android.os.RemoteException; -import android.util.Log; - -import java.util.HashMap; - -/** - * This class provides the primary API for DTA operations. - * @hide - */ -public final class NfcDta { - private static final String TAG = "NfcDta"; - - private static INfcDta sService; - private static HashMap<Context, NfcDta> sNfcDtas = new HashMap<Context, NfcDta>(); - - private final Context mContext; - - private NfcDta(Context context, INfcDta service) { - mContext = context.getApplicationContext(); - sService = service; - } - - /** - * Helper to get an instance of this class. - * - * @param adapter A reference to an NfcAdapter object. - * @return - */ - public static synchronized NfcDta getInstance(NfcAdapter adapter) { - if (adapter == null) throw new NullPointerException("NfcAdapter is null"); - Context context = adapter.getContext(); - if (context == null) { - Log.e(TAG, "NfcAdapter context is null."); - throw new UnsupportedOperationException(); - } - - NfcDta manager = sNfcDtas.get(context); - if (manager == null) { - INfcDta service = adapter.getNfcDtaInterface(); - if (service == null) { - Log.e(TAG, "This device does not implement the INfcDta interface."); - throw new UnsupportedOperationException(); - } - manager = new NfcDta(context, service); - sNfcDtas.put(context, manager); - } - return manager; - } - - /** - * Enables DTA mode - * - * @return true/false if enabling was successful - */ - public boolean enableDta() { - try { - sService.enableDta(); - } catch (RemoteException e) { - return false; - } - return true; - } - - /** - * Disables DTA mode - * - * @return true/false if disabling was successful - */ - public boolean disableDta() { - try { - sService.disableDta(); - } catch (RemoteException e) { - return false; - } - return true; - } - - /** - * Enables Server - * - * @return true/false if enabling was successful - */ - public boolean enableServer(String serviceName, int serviceSap, int miu, - int rwSize, int testCaseId) { - try { - return sService.enableServer(serviceName, serviceSap, miu, rwSize, testCaseId); - } catch (RemoteException e) { - return false; - } - } - - /** - * Disables Server - * - * @return true/false if disabling was successful - */ - public boolean disableServer() { - try { - sService.disableServer(); - } catch (RemoteException e) { - return false; - } - return true; - } - - /** - * Enables Client - * - * @return true/false if enabling was successful - */ - public boolean enableClient(String serviceName, int miu, int rwSize, - int testCaseId) { - try { - return sService.enableClient(serviceName, miu, rwSize, testCaseId); - } catch (RemoteException e) { - return false; - } - } - - /** - * Disables client - * - * @return true/false if disabling was successful - */ - public boolean disableClient() { - try { - sService.disableClient(); - } catch (RemoteException e) { - return false; - } - return true; - } - - /** - * Registers Message Service - * - * @return true/false if registration was successful - */ - public boolean registerMessageService(String msgServiceName) { - try { - return sService.registerMessageService(msgServiceName); - } catch (RemoteException e) { - return false; - } - } -} diff --git a/nfc/java/android/nfc/package.html b/nfc/java/android/nfc/package.html deleted file mode 100644 index 55c1d1650aa9..000000000000 --- a/nfc/java/android/nfc/package.html +++ /dev/null @@ -1,33 +0,0 @@ -<HTML> -<BODY> -<p>Provides access to Near Field Communication (NFC) functionality, allowing applications to read -NDEF message in NFC tags. A "tag" may actually be another device that appears as a tag.</p> - -<p>For more information, see the -<a href="{@docRoot}guide/topics/connectivity/nfc/index.html">Near Field Communication</a> guide.</p> -{@more} - -<p>Here's a summary of the classes:</p> - -<dl> - <dt>{@link android.nfc.NfcManager}</dt> - <dd>This is the high level manager, used to obtain this device's {@link android.nfc.NfcAdapter}. You can -acquire an instance using {@link android.content.Context#getSystemService}.</dd> - <dt>{@link android.nfc.NfcAdapter}</dt> - <dd>This represents the device's NFC adapter, which is your entry-point to performing NFC -operations. You can acquire an instance with {@link android.nfc.NfcManager#getDefaultAdapter}, or -{@link android.nfc.NfcAdapter#getDefaultAdapter(android.content.Context)}.</dd> - <dt>{@link android.nfc.NdefMessage}</dt> - <dd>Represents an NDEF data message, which is the standard format in which "records" -carrying data are transmitted between devices and tags. Your application can receive these -messages from an {@link android.nfc.NfcAdapter#ACTION_TAG_DISCOVERED} intent.</dd> - <dt>{@link android.nfc.NdefRecord}</dt> - <dd>Represents a record, which is delivered in a {@link android.nfc.NdefMessage} and describes the -type of data being shared and carries the data itself.</dd> -</dl> - -<p class="note"><strong>Note:</strong> -Not all Android-powered devices provide NFC functionality.</p> - -</BODY> -</HTML> diff --git a/nfc/java/android/nfc/tech/BasicTagTechnology.java b/nfc/java/android/nfc/tech/BasicTagTechnology.java deleted file mode 100644 index ae468fead7a2..000000000000 --- a/nfc/java/android/nfc/tech/BasicTagTechnology.java +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright (C) 2010 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.tech; - -import android.nfc.ErrorCodes; -import android.nfc.Tag; -import android.nfc.TransceiveResult; -import android.os.RemoteException; -import android.util.Log; - -import java.io.IOException; - -/** - * A base class for tag technologies that are built on top of transceive(). - */ -abstract class BasicTagTechnology implements TagTechnology { - private static final String TAG = "NFC"; - - final Tag mTag; - - boolean mIsConnected; - int mSelectedTechnology; - - BasicTagTechnology(Tag tag, int tech) throws RemoteException { - mTag = tag; - mSelectedTechnology = tech; - } - - @Override - public Tag getTag() { - return mTag; - } - - /** Internal helper to throw IllegalStateException if the technology isn't connected */ - void checkConnected() { - if ((mTag.getConnectedTechnology() != mSelectedTechnology) || - (mTag.getConnectedTechnology() == -1)) { - throw new IllegalStateException("Call connect() first!"); - } - } - - @Override - public boolean isConnected() { - if (!mIsConnected) { - return false; - } - - try { - return mTag.getTagService().isPresent(mTag.getServiceHandle()); - } catch (RemoteException e) { - Log.e(TAG, "NFC service dead", e); - return false; - } - } - - @Override - public void connect() throws IOException { - try { - int errorCode = mTag.getTagService().connect(mTag.getServiceHandle(), - mSelectedTechnology); - - if (errorCode == ErrorCodes.SUCCESS) { - // Store this in the tag object - if (!mTag.setConnectedTechnology(mSelectedTechnology)) { - Log.e(TAG, "Close other technology first!"); - throw new IOException("Only one TagTechnology can be connected at a time."); - } - mIsConnected = true; - } else if (errorCode == ErrorCodes.ERROR_NOT_SUPPORTED) { - throw new UnsupportedOperationException("Connecting to " + - "this technology is not supported by the NFC " + - "adapter."); - } else { - throw new IOException(); - } - } catch (RemoteException e) { - Log.e(TAG, "NFC service dead", e); - throw new IOException("NFC service died"); - } - } - - /** @hide */ - @Override - public void reconnect() throws IOException { - if (!mIsConnected) { - throw new IllegalStateException("Technology not connected yet"); - } - - try { - int errorCode = mTag.getTagService().reconnect(mTag.getServiceHandle()); - - if (errorCode != ErrorCodes.SUCCESS) { - mIsConnected = false; - mTag.setTechnologyDisconnected(); - throw new IOException(); - } - } catch (RemoteException e) { - mIsConnected = false; - mTag.setTechnologyDisconnected(); - Log.e(TAG, "NFC service dead", e); - throw new IOException("NFC service died"); - } - } - - @Override - public void close() throws IOException { - try { - /* Note that we don't want to physically disconnect the tag, - * but just reconnect to it to reset its state - */ - mTag.getTagService().resetTimeouts(); - mTag.getTagService().reconnect(mTag.getServiceHandle()); - } catch (RemoteException e) { - Log.e(TAG, "NFC service dead", e); - } finally { - mIsConnected = false; - mTag.setTechnologyDisconnected(); - } - } - - /** Internal getMaxTransceiveLength() */ - int getMaxTransceiveLengthInternal() { - try { - return mTag.getTagService().getMaxTransceiveLength(mSelectedTechnology); - } catch (RemoteException e) { - Log.e(TAG, "NFC service dead", e); - return 0; - } - } - /** Internal transceive */ - byte[] transceive(byte[] data, boolean raw) throws IOException { - checkConnected(); - - try { - TransceiveResult result = mTag.getTagService().transceive(mTag.getServiceHandle(), - data, raw); - if (result == null) { - throw new IOException("transceive failed"); - } else { - return result.getResponseOrThrow(); - } - } catch (RemoteException e) { - Log.e(TAG, "NFC service dead", e); - throw new IOException("NFC service died"); - } - } -} diff --git a/nfc/java/android/nfc/tech/IsoDep.java b/nfc/java/android/nfc/tech/IsoDep.java deleted file mode 100644 index 0ba0c5a8d13b..000000000000 --- a/nfc/java/android/nfc/tech/IsoDep.java +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright (C) 2010 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.tech; - -import android.nfc.ErrorCodes; -import android.nfc.Tag; -import android.os.Bundle; -import android.os.RemoteException; -import android.util.Log; - -import java.io.IOException; - -/** - * Provides access to ISO-DEP (ISO 14443-4) properties and I/O operations on a {@link Tag}. - * - * <p>Acquire an {@link IsoDep} object using {@link #get}. - * <p>The primary ISO-DEP I/O operation is {@link #transceive}. Applications must - * implement their own protocol stack on top of {@link #transceive}. - * <p>Tags that enumerate the {@link IsoDep} technology in {@link Tag#getTechList} - * will also enumerate - * {@link NfcA} or {@link NfcB} (since IsoDep builds on top of either of these). - * - * <p class="note"><strong>Note:</strong> Methods that perform I/O operations - * require the {@link android.Manifest.permission#NFC} permission. - */ -public final class IsoDep extends BasicTagTechnology { - private static final String TAG = "NFC"; - - /** @hide */ - public static final String EXTRA_HI_LAYER_RESP = "hiresp"; - /** @hide */ - public static final String EXTRA_HIST_BYTES = "histbytes"; - - private byte[] mHiLayerResponse = null; - private byte[] mHistBytes = null; - - /** - * Get an instance of {@link IsoDep} for the given tag. - * <p>Does not cause any RF activity and does not block. - * <p>Returns null if {@link IsoDep} was not enumerated in {@link Tag#getTechList}. - * This indicates the tag does not support ISO-DEP. - * - * @param tag an ISO-DEP compatible tag - * @return ISO-DEP object - */ - public static IsoDep get(Tag tag) { - if (!tag.hasTech(TagTechnology.ISO_DEP)) return null; - try { - return new IsoDep(tag); - } catch (RemoteException e) { - return null; - } - } - - /** @hide */ - public IsoDep(Tag tag) - throws RemoteException { - super(tag, TagTechnology.ISO_DEP); - Bundle extras = tag.getTechExtras(TagTechnology.ISO_DEP); - if (extras != null) { - mHiLayerResponse = extras.getByteArray(EXTRA_HI_LAYER_RESP); - mHistBytes = extras.getByteArray(EXTRA_HIST_BYTES); - } - } - - /** - * Set the timeout of {@link #transceive} in milliseconds. - * <p>The timeout only applies to ISO-DEP {@link #transceive}, and is - * reset to a default value when {@link #close} is called. - * <p>Setting a longer timeout may be useful when performing - * transactions that require a long processing time on the tag - * such as key generation. - * - * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. - * - * @param timeout timeout value in milliseconds - * @throws SecurityException if the tag object is reused after the tag has left the field - */ - public void setTimeout(int timeout) { - try { - int err = mTag.getTagService().setTimeout(TagTechnology.ISO_DEP, timeout); - if (err != ErrorCodes.SUCCESS) { - throw new IllegalArgumentException("The supplied timeout is not valid"); - } - } catch (RemoteException e) { - Log.e(TAG, "NFC service dead", e); - } - } - - /** - * Get the current timeout for {@link #transceive} in milliseconds. - * - * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. - * - * @return timeout value in milliseconds - * @throws SecurityException if the tag object is reused after the tag has left the field - */ - public int getTimeout() { - try { - return mTag.getTagService().getTimeout(TagTechnology.ISO_DEP); - } catch (RemoteException e) { - Log.e(TAG, "NFC service dead", e); - return 0; - } - } - - /** - * Return the ISO-DEP historical bytes for {@link NfcA} tags. - * <p>Does not cause any RF activity and does not block. - * <p>The historical bytes can be used to help identify a tag. They are present - * only on {@link IsoDep} tags that are based on {@link NfcA} RF technology. - * If this tag is not {@link NfcA} then null is returned. - * <p>In ISO 14443-4 terminology, the historical bytes are a subset of the RATS - * response. - * - * @return ISO-DEP historical bytes, or null if this is not a {@link NfcA} tag - */ - public byte[] getHistoricalBytes() { - return mHistBytes; - } - - /** - * Return the higher layer response bytes for {@link NfcB} tags. - * <p>Does not cause any RF activity and does not block. - * <p>The higher layer response bytes can be used to help identify a tag. - * They are present only on {@link IsoDep} tags that are based on {@link NfcB} - * RF technology. If this tag is not {@link NfcB} then null is returned. - * <p>In ISO 14443-4 terminology, the higher layer bytes are a subset of the - * ATTRIB response. - * - * @return ISO-DEP historical bytes, or null if this is not a {@link NfcB} tag - */ - public byte[] getHiLayerResponse() { - return mHiLayerResponse; - } - - /** - * Send raw ISO-DEP data to the tag and receive the response. - * - * <p>Applications must only send the INF payload, and not the start of frame and - * end of frame indicators. Applications do not need to fragment the payload, it - * will be automatically fragmented and defragmented by {@link #transceive} if - * it exceeds FSD/FSC limits. - * - * <p>Use {@link #getMaxTransceiveLength} to retrieve the maximum number of bytes - * that can be sent with {@link #transceive}. - * - * <p>This is an I/O operation and will block until complete. It must - * not be called from the main application thread. A blocked call will be canceled with - * {@link IOException} if {@link #close} is called from another thread. - * - * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. - * - * @param data command bytes to send, must not be null - * @return response bytes received, will not be null - * @throws TagLostException if the tag leaves the field - * @throws IOException if there is an I/O failure, or this operation is canceled - * @throws SecurityException if the tag object is reused after the tag has left the field - */ - public byte[] transceive(byte[] data) throws IOException { - return transceive(data, true); - } - - /** - * Return the maximum number of bytes that can be sent with {@link #transceive}. - * @return the maximum number of bytes that can be sent with {@link #transceive}. - */ - public int getMaxTransceiveLength() { - return getMaxTransceiveLengthInternal(); - } - - /** - * <p>Standard APDUs have a 1-byte length field, allowing a maximum of - * 255 payload bytes, which results in a maximum APDU length of 261 bytes. - * - * <p>Extended length APDUs have a 3-byte length field, allowing 65535 - * payload bytes. - * - * <p>Some NFC adapters, like the one used in the Nexus S and the Galaxy Nexus - * do not support extended length APDUs. They are expected to be well-supported - * in the future though. Use this method to check for extended length APDU - * support. - * - * @return whether the NFC adapter on this device supports extended length APDUs. - * @throws SecurityException if the tag object is reused after the tag has left the field - */ - public boolean isExtendedLengthApduSupported() { - try { - return mTag.getTagService().getExtendedLengthApdusSupported(); - } catch (RemoteException e) { - Log.e(TAG, "NFC service dead", e); - return false; - } - } -} diff --git a/nfc/java/android/nfc/tech/MifareClassic.java b/nfc/java/android/nfc/tech/MifareClassic.java deleted file mode 100644 index 26f54e692289..000000000000 --- a/nfc/java/android/nfc/tech/MifareClassic.java +++ /dev/null @@ -1,655 +0,0 @@ -/* - * Copyright (C) 2010 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.tech; - -import android.nfc.ErrorCodes; -import android.nfc.Tag; -import android.nfc.TagLostException; -import android.os.RemoteException; -import android.util.Log; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; - -/** - * Provides access to MIFARE Classic properties and I/O operations on a {@link Tag}. - * - * <p>Acquire a {@link MifareClassic} object using {@link #get}. - * - * <p>MIFARE Classic is also known as MIFARE Standard. - * <p>MIFARE Classic tags are divided into sectors, and each sector is sub-divided into - * blocks. Block size is always 16 bytes ({@link #BLOCK_SIZE}. Sector size varies. - * <ul> - * <li>MIFARE Classic Mini are 320 bytes ({@link #SIZE_MINI}), with 5 sectors each of 4 blocks. - * <li>MIFARE Classic 1k are 1024 bytes ({@link #SIZE_1K}), with 16 sectors each of 4 blocks. - * <li>MIFARE Classic 2k are 2048 bytes ({@link #SIZE_2K}), with 32 sectors each of 4 blocks. - * <li>MIFARE Classic 4k are 4096 bytes ({@link #SIZE_4K}). The first 32 sectors contain 4 blocks - * and the last 8 sectors contain 16 blocks. - * </ul> - * - * <p>MIFARE Classic tags require authentication on a per-sector basis before any - * other I/O operations on that sector can be performed. There are two keys per sector, - * and ACL bits determine what I/O operations are allowed on that sector after - * authenticating with a key. {@see #authenticateSectorWithKeyA} and - * {@see #authenticateSectorWithKeyB}. - * - * <p>Three well-known authentication keys are defined in this class: - * {@link #KEY_DEFAULT}, {@link #KEY_MIFARE_APPLICATION_DIRECTORY}, - * {@link #KEY_NFC_FORUM}. - * <ul> - * <li>{@link #KEY_DEFAULT} is the default factory key for MIFARE Classic. - * <li>{@link #KEY_MIFARE_APPLICATION_DIRECTORY} is the well-known key for - * MIFARE Classic cards that have been formatted according to the - * MIFARE Application Directory (MAD) specification. - * <li>{@link #KEY_NFC_FORUM} is the well-known key for MIFARE Classic cards that - * have been formatted according to the NXP specification for NDEF on MIFARE Classic. - * - * <p>Implementation of this class on a Android NFC device is optional. - * If it is not implemented, then - * {@link MifareClassic} will never be enumerated in {@link Tag#getTechList}. - * If it is enumerated, then all {@link MifareClassic} I/O operations will be supported, - * and {@link Ndef#MIFARE_CLASSIC} NDEF tags will also be supported. In either case, - * {@link NfcA} will also be enumerated on the tag, because all MIFARE Classic tags are also - * {@link NfcA}. - * - * <p class="note"><strong>Note:</strong> Methods that perform I/O operations - * require the {@link android.Manifest.permission#NFC} permission. - */ -public final class MifareClassic extends BasicTagTechnology { - private static final String TAG = "NFC"; - - /** - * The default factory key. - */ - public static final byte[] KEY_DEFAULT = - {(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF}; - /** - * The well-known key for tags formatted according to the - * MIFARE Application Directory (MAD) specification. - */ - public static final byte[] KEY_MIFARE_APPLICATION_DIRECTORY = - {(byte)0xA0,(byte)0xA1,(byte)0xA2,(byte)0xA3,(byte)0xA4,(byte)0xA5}; - /** - * The well-known key for tags formatted according to the - * NDEF on MIFARE Classic specification. - */ - public static final byte[] KEY_NFC_FORUM = - {(byte)0xD3,(byte)0xF7,(byte)0xD3,(byte)0xF7,(byte)0xD3,(byte)0xF7}; - - /** A MIFARE Classic compatible card of unknown type */ - public static final int TYPE_UNKNOWN = -1; - /** A MIFARE Classic tag */ - public static final int TYPE_CLASSIC = 0; - /** A MIFARE Plus tag */ - public static final int TYPE_PLUS = 1; - /** A MIFARE Pro tag */ - public static final int TYPE_PRO = 2; - - /** Tag contains 16 sectors, each with 4 blocks. */ - public static final int SIZE_1K = 1024; - /** Tag contains 32 sectors, each with 4 blocks. */ - public static final int SIZE_2K = 2048; - /** - * Tag contains 40 sectors. The first 32 sectors contain 4 blocks and the last 8 sectors - * contain 16 blocks. - */ - public static final int SIZE_4K = 4096; - /** Tag contains 5 sectors, each with 4 blocks. */ - public static final int SIZE_MINI = 320; - - /** Size of a MIFARE Classic block (in bytes) */ - public static final int BLOCK_SIZE = 16; - - private static final int MAX_BLOCK_COUNT = 256; - private static final int MAX_SECTOR_COUNT = 40; - - private boolean mIsEmulated; - private int mType; - private int mSize; - - /** - * Get an instance of {@link MifareClassic} for the given tag. - * <p>Does not cause any RF activity and does not block. - * <p>Returns null if {@link MifareClassic} was not enumerated in {@link Tag#getTechList}. - * This indicates the tag is not MIFARE Classic compatible, or this Android - * device does not support MIFARE Classic. - * - * @param tag an MIFARE Classic compatible tag - * @return MIFARE Classic object - */ - public static MifareClassic get(Tag tag) { - if (!tag.hasTech(TagTechnology.MIFARE_CLASSIC)) return null; - try { - return new MifareClassic(tag); - } catch (RemoteException e) { - return null; - } - } - - /** @hide */ - public MifareClassic(Tag tag) throws RemoteException { - super(tag, TagTechnology.MIFARE_CLASSIC); - - NfcA a = NfcA.get(tag); // MIFARE Classic is always based on NFC a - - mIsEmulated = false; - - switch (a.getSak()) { - case 0x01: - case 0x08: - mType = TYPE_CLASSIC; - mSize = SIZE_1K; - break; - case 0x09: - mType = TYPE_CLASSIC; - mSize = SIZE_MINI; - break; - case 0x10: - mType = TYPE_PLUS; - mSize = SIZE_2K; - // SecLevel = SL2 - break; - case 0x11: - mType = TYPE_PLUS; - mSize = SIZE_4K; - // Seclevel = SL2 - break; - case 0x18: - mType = TYPE_CLASSIC; - mSize = SIZE_4K; - break; - case 0x28: - mType = TYPE_CLASSIC; - mSize = SIZE_1K; - mIsEmulated = true; - break; - case 0x38: - mType = TYPE_CLASSIC; - mSize = SIZE_4K; - mIsEmulated = true; - break; - case 0x88: - mType = TYPE_CLASSIC; - mSize = SIZE_1K; - // NXP-tag: false - break; - case 0x98: - case 0xB8: - mType = TYPE_PRO; - mSize = SIZE_4K; - break; - default: - // Stack incorrectly reported a MifareClassic. We cannot handle this - // gracefully - we have no idea of the memory layout. Bail. - throw new RuntimeException( - "Tag incorrectly enumerated as MIFARE Classic, SAK = " + a.getSak()); - } - } - - /** - * Return the type of this MIFARE Classic compatible tag. - * <p>One of {@link #TYPE_UNKNOWN}, {@link #TYPE_CLASSIC}, {@link #TYPE_PLUS} or - * {@link #TYPE_PRO}. - * <p>Does not cause any RF activity and does not block. - * - * @return type - */ - public int getType() { - return mType; - } - - /** - * Return the size of the tag in bytes - * <p>One of {@link #SIZE_MINI}, {@link #SIZE_1K}, {@link #SIZE_2K}, {@link #SIZE_4K}. - * These constants are equal to their respective size in bytes. - * <p>Does not cause any RF activity and does not block. - * @return size in bytes - */ - public int getSize() { - return mSize; - } - - /** - * Return true if the tag is emulated, determined at discovery time. - * These are actually smart-cards that emulate a MIFARE Classic interface. - * They can be treated identically to a MIFARE Classic tag. - * @hide - */ - public boolean isEmulated() { - return mIsEmulated; - } - - /** - * Return the number of MIFARE Classic sectors. - * <p>Does not cause any RF activity and does not block. - * @return number of sectors - */ - public int getSectorCount() { - switch (mSize) { - case SIZE_1K: - return 16; - case SIZE_2K: - return 32; - case SIZE_4K: - return 40; - case SIZE_MINI: - return 5; - default: - return 0; - } - } - - /** - * Return the total number of MIFARE Classic blocks. - * <p>Does not cause any RF activity and does not block. - * @return total number of blocks - */ - public int getBlockCount() { - return mSize / BLOCK_SIZE; - } - - /** - * Return the number of blocks in the given sector. - * <p>Does not cause any RF activity and does not block. - * - * @param sectorIndex index of sector, starting from 0 - * @return number of blocks in the sector - */ - public int getBlockCountInSector(int sectorIndex) { - validateSector(sectorIndex); - - if (sectorIndex < 32) { - return 4; - } else { - return 16; - } - } - - /** - * Return the sector that contains a given block. - * <p>Does not cause any RF activity and does not block. - * - * @param blockIndex index of block to lookup, starting from 0 - * @return sector index that contains the block - */ - public int blockToSector(int blockIndex) { - validateBlock(blockIndex); - - if (blockIndex < 32 * 4) { - return blockIndex / 4; - } else { - return 32 + (blockIndex - 32 * 4) / 16; - } - } - - /** - * Return the first block of a given sector. - * <p>Does not cause any RF activity and does not block. - * - * @param sectorIndex index of sector to lookup, starting from 0 - * @return block index of first block in sector - */ - public int sectorToBlock(int sectorIndex) { - if (sectorIndex < 32) { - return sectorIndex * 4; - } else { - return 32 * 4 + (sectorIndex - 32) * 16; - } - } - - /** - * Authenticate a sector with key A. - * - * <p>Successful authentication of a sector with key A enables other - * I/O operations on that sector. The set of operations granted by key A - * key depends on the ACL bits set in that sector. For more information - * see the MIFARE Classic specification on <a href="http://www.nxp.com">http://www.nxp.com</a>. - * - * <p>A failed authentication attempt causes an implicit reconnection to the - * tag, so authentication to other sectors will be lost. - * - * <p>This is an I/O operation and will block until complete. It must - * not be called from the main application thread. A blocked call will be canceled with - * {@link IOException} if {@link #close} is called from another thread. - * - * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. - * - * @param sectorIndex index of sector to authenticate, starting from 0 - * @param key 6-byte authentication key - * @return true on success, false on authentication failure - * @throws TagLostException if the tag leaves the field - * @throws IOException if there is an I/O failure, or the operation is canceled - */ - public boolean authenticateSectorWithKeyA(int sectorIndex, byte[] key) throws IOException { - return authenticate(sectorIndex, key, true); - } - - /** - * Authenticate a sector with key B. - * - * <p>Successful authentication of a sector with key B enables other - * I/O operations on that sector. The set of operations granted by key B - * depends on the ACL bits set in that sector. For more information - * see the MIFARE Classic specification on <a href="http://www.nxp.com">http://www.nxp.com</a>. - * - * <p>A failed authentication attempt causes an implicit reconnection to the - * tag, so authentication to other sectors will be lost. - * - * <p>This is an I/O operation and will block until complete. It must - * not be called from the main application thread. A blocked call will be canceled with - * {@link IOException} if {@link #close} is called from another thread. - * - * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. - * - * @param sectorIndex index of sector to authenticate, starting from 0 - * @param key 6-byte authentication key - * @return true on success, false on authentication failure - * @throws TagLostException if the tag leaves the field - * @throws IOException if there is an I/O failure, or the operation is canceled - */ - public boolean authenticateSectorWithKeyB(int sectorIndex, byte[] key) throws IOException { - return authenticate(sectorIndex, key, false); - } - - private boolean authenticate(int sector, byte[] key, boolean keyA) throws IOException { - validateSector(sector); - checkConnected(); - - byte[] cmd = new byte[12]; - - // First byte is the command - if (keyA) { - cmd[0] = 0x60; // phHal_eMifareAuthentA - } else { - cmd[0] = 0x61; // phHal_eMifareAuthentB - } - - // Second byte is block address - // Authenticate command takes a block address. Authenticating a block - // of a sector will authenticate the entire sector. - cmd[1] = (byte) sectorToBlock(sector); - - // Next 4 bytes are last 4 bytes of UID - byte[] uid = getTag().getId(); - System.arraycopy(uid, uid.length - 4, cmd, 2, 4); - - // Next 6 bytes are key - System.arraycopy(key, 0, cmd, 6, 6); - - try { - if (transceive(cmd, false) != null) { - return true; - } - } catch (TagLostException e) { - throw e; - } catch (IOException e) { - // No need to deal with, will return false anyway - } - return false; - } - - /** - * Read 16-byte block. - * - * <p>This is an I/O operation and will block until complete. It must - * not be called from the main application thread. A blocked call will be canceled with - * {@link IOException} if {@link #close} is called from another thread. - * - * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. - * - * @param blockIndex index of block to read, starting from 0 - * @return 16 byte block - * @throws TagLostException if the tag leaves the field - * @throws IOException if there is an I/O failure, or the operation is canceled - */ - public byte[] readBlock(int blockIndex) throws IOException { - validateBlock(blockIndex); - checkConnected(); - - byte[] cmd = { 0x30, (byte) blockIndex }; - return transceive(cmd, false); - } - - /** - * Write 16-byte block. - * - * <p>This is an I/O operation and will block until complete. It must - * not be called from the main application thread. A blocked call will be canceled with - * {@link IOException} if {@link #close} is called from another thread. - * - * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. - * - * @param blockIndex index of block to write, starting from 0 - * @param data 16 bytes of data to write - * @throws TagLostException if the tag leaves the field - * @throws IOException if there is an I/O failure, or the operation is canceled - */ - public void writeBlock(int blockIndex, byte[] data) throws IOException { - validateBlock(blockIndex); - checkConnected(); - if (data.length != 16) { - throw new IllegalArgumentException("must write 16-bytes"); - } - - byte[] cmd = new byte[data.length + 2]; - cmd[0] = (byte) 0xA0; // MF write command - cmd[1] = (byte) blockIndex; - System.arraycopy(data, 0, cmd, 2, data.length); - - transceive(cmd, false); - } - - /** - * Increment a value block, storing the result in the temporary block on the tag. - * - * <p>This is an I/O operation and will block until complete. It must - * not be called from the main application thread. A blocked call will be canceled with - * {@link IOException} if {@link #close} is called from another thread. - * - * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. - * - * @param blockIndex index of block to increment, starting from 0 - * @param value non-negative to increment by - * @throws TagLostException if the tag leaves the field - * @throws IOException if there is an I/O failure, or the operation is canceled - */ - public void increment(int blockIndex, int value) throws IOException { - validateBlock(blockIndex); - validateValueOperand(value); - checkConnected(); - - ByteBuffer cmd = ByteBuffer.allocate(6); - cmd.order(ByteOrder.LITTLE_ENDIAN); - cmd.put( (byte) 0xC1 ); - cmd.put( (byte) blockIndex ); - cmd.putInt(value); - - transceive(cmd.array(), false); - } - - /** - * Decrement a value block, storing the result in the temporary block on the tag. - * - * <p>This is an I/O operation and will block until complete. It must - * not be called from the main application thread. A blocked call will be canceled with - * {@link IOException} if {@link #close} is called from another thread. - * - * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. - * - * @param blockIndex index of block to decrement, starting from 0 - * @param value non-negative to decrement by - * @throws TagLostException if the tag leaves the field - * @throws IOException if there is an I/O failure, or the operation is canceled - */ - public void decrement(int blockIndex, int value) throws IOException { - validateBlock(blockIndex); - validateValueOperand(value); - checkConnected(); - - ByteBuffer cmd = ByteBuffer.allocate(6); - cmd.order(ByteOrder.LITTLE_ENDIAN); - cmd.put( (byte) 0xC0 ); - cmd.put( (byte) blockIndex ); - cmd.putInt(value); - - transceive(cmd.array(), false); - } - - /** - * Copy from the temporary block to a value block. - * - * <p>This is an I/O operation and will block until complete. It must - * not be called from the main application thread. A blocked call will be canceled with - * {@link IOException} if {@link #close} is called from another thread. - * - * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. - * - * @param blockIndex index of block to copy to - * @throws TagLostException if the tag leaves the field - * @throws IOException if there is an I/O failure, or the operation is canceled - */ - public void transfer(int blockIndex) throws IOException { - validateBlock(blockIndex); - checkConnected(); - - byte[] cmd = { (byte) 0xB0, (byte) blockIndex }; - - transceive(cmd, false); - } - - /** - * Copy from a value block to the temporary block. - * - * <p>This is an I/O operation and will block until complete. It must - * not be called from the main application thread. A blocked call will be canceled with - * {@link IOException} if {@link #close} is called from another thread. - * - * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. - * - * @param blockIndex index of block to copy from - * @throws TagLostException if the tag leaves the field - * @throws IOException if there is an I/O failure, or the operation is canceled - */ - public void restore(int blockIndex) throws IOException { - validateBlock(blockIndex); - checkConnected(); - - byte[] cmd = { (byte) 0xC2, (byte) blockIndex }; - - transceive(cmd, false); - } - - /** - * Send raw NfcA data to a tag and receive the response. - * - * <p>This is equivalent to connecting to this tag via {@link NfcA} - * and calling {@link NfcA#transceive}. Note that all MIFARE Classic - * tags are based on {@link NfcA} technology. - * - * <p>Use {@link #getMaxTransceiveLength} to retrieve the maximum number of bytes - * that can be sent with {@link #transceive}. - * - * <p>This is an I/O operation and will block until complete. It must - * not be called from the main application thread. A blocked call will be canceled with - * {@link IOException} if {@link #close} is called from another thread. - * - * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. - * - * @see NfcA#transceive - */ - public byte[] transceive(byte[] data) throws IOException { - return transceive(data, true); - } - - /** - * Return the maximum number of bytes that can be sent with {@link #transceive}. - * @return the maximum number of bytes that can be sent with {@link #transceive}. - */ - public int getMaxTransceiveLength() { - return getMaxTransceiveLengthInternal(); - } - - /** - * Set the {@link #transceive} timeout in milliseconds. - * - * <p>The timeout only applies to {@link #transceive} on this object, - * and is reset to a default value when {@link #close} is called. - * - * <p>Setting a longer timeout may be useful when performing - * transactions that require a long processing time on the tag - * such as key generation. - * - * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. - * - * @param timeout timeout value in milliseconds - * @throws SecurityException if the tag object is reused after the tag has left the field - */ - public void setTimeout(int timeout) { - try { - int err = mTag.getTagService().setTimeout(TagTechnology.MIFARE_CLASSIC, timeout); - if (err != ErrorCodes.SUCCESS) { - throw new IllegalArgumentException("The supplied timeout is not valid"); - } - } catch (RemoteException e) { - Log.e(TAG, "NFC service dead", e); - } - } - - /** - * Get the current {@link #transceive} timeout in milliseconds. - * - * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. - * - * @return timeout value in milliseconds - * @throws SecurityException if the tag object is reused after the tag has left the field - */ - public int getTimeout() { - try { - return mTag.getTagService().getTimeout(TagTechnology.MIFARE_CLASSIC); - } catch (RemoteException e) { - Log.e(TAG, "NFC service dead", e); - return 0; - } - } - - private static void validateSector(int sector) { - // Do not be too strict on upper bounds checking, since some cards - // have more addressable memory than they report. For example, - // MIFARE Plus 2k cards will appear as MIFARE Classic 1k cards when in - // MIFARE Classic compatibility mode. - // Note that issuing a command to an out-of-bounds block is safe - the - // tag should report error causing IOException. This validation is a - // helper to guard against obvious programming mistakes. - if (sector < 0 || sector >= MAX_SECTOR_COUNT) { - throw new IndexOutOfBoundsException("sector out of bounds: " + sector); - } - } - - private static void validateBlock(int block) { - // Just looking for obvious out of bounds... - if (block < 0 || block >= MAX_BLOCK_COUNT) { - throw new IndexOutOfBoundsException("block out of bounds: " + block); - } - } - - private static void validateValueOperand(int value) { - if (value < 0) { - throw new IllegalArgumentException("value operand negative"); - } - } -} diff --git a/nfc/java/android/nfc/tech/MifareUltralight.java b/nfc/java/android/nfc/tech/MifareUltralight.java deleted file mode 100644 index c0416a39ba76..000000000000 --- a/nfc/java/android/nfc/tech/MifareUltralight.java +++ /dev/null @@ -1,280 +0,0 @@ -/* - * Copyright (C) 2010 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.tech; - -import android.nfc.ErrorCodes; -import android.nfc.Tag; -import android.nfc.TagLostException; -import android.os.Bundle; -import android.os.RemoteException; -import android.util.Log; - -import java.io.IOException; - -//TOOD: Ultralight C 3-DES authentication, one-way counter - -/** - * Provides access to MIFARE Ultralight properties and I/O operations on a {@link Tag}. - * - * <p>Acquire a {@link MifareUltralight} object using {@link #get}. - * - * <p>MIFARE Ultralight compatible tags have 4 byte pages {@link #PAGE_SIZE}. - * The primary operations on an Ultralight tag are {@link #readPages} and - * {@link #writePage}. - * - * <p>The original MIFARE Ultralight consists of a 64 byte EEPROM. The first - * 4 pages are for the OTP area, manufacturer data, and locking bits. They are - * readable and some bits are writable. The final 12 pages are the user - * read/write area. For more information see the NXP data sheet MF0ICU1. - * - * <p>The MIFARE Ultralight C consists of a 192 byte EEPROM. The first 4 pages - * are for OTP, manufacturer data, and locking bits. The next 36 pages are the - * user read/write area. The next 4 pages are additional locking bits, counters - * and authentication configuration and are readable. The final 4 pages are for - * the authentication key and are not readable. For more information see the - * NXP data sheet MF0ICU2. - * - * <p>Implementation of this class on a Android NFC device is optional. - * If it is not implemented, then - * {@link MifareUltralight} will never be enumerated in {@link Tag#getTechList}. - * If it is enumerated, then all {@link MifareUltralight} I/O operations will be supported. - * In either case, {@link NfcA} will also be enumerated on the tag, - * because all MIFARE Ultralight tags are also {@link NfcA} tags. - * - * <p class="note"><strong>Note:</strong> Methods that perform I/O operations - * require the {@link android.Manifest.permission#NFC} permission. - */ -public final class MifareUltralight extends BasicTagTechnology { - private static final String TAG = "NFC"; - - /** A MIFARE Ultralight compatible tag of unknown type */ - public static final int TYPE_UNKNOWN = -1; - /** A MIFARE Ultralight tag */ - public static final int TYPE_ULTRALIGHT = 1; - /** A MIFARE Ultralight C tag */ - public static final int TYPE_ULTRALIGHT_C = 2; - - /** Size of a MIFARE Ultralight page in bytes */ - public static final int PAGE_SIZE = 4; - - private static final int NXP_MANUFACTURER_ID = 0x04; - private static final int MAX_PAGE_COUNT = 256; - - /** @hide */ - public static final String EXTRA_IS_UL_C = "isulc"; - - private int mType; - - /** - * Get an instance of {@link MifareUltralight} for the given tag. - * <p>Returns null if {@link MifareUltralight} was not enumerated in - * {@link Tag#getTechList} - this indicates the tag is not MIFARE - * Ultralight compatible, or that this Android - * device does not implement MIFARE Ultralight. - * <p>Does not cause any RF activity and does not block. - * - * @param tag an MIFARE Ultralight compatible tag - * @return MIFARE Ultralight object - */ - public static MifareUltralight get(Tag tag) { - if (!tag.hasTech(TagTechnology.MIFARE_ULTRALIGHT)) return null; - try { - return new MifareUltralight(tag); - } catch (RemoteException e) { - return null; - } - } - - /** @hide */ - public MifareUltralight(Tag tag) throws RemoteException { - super(tag, TagTechnology.MIFARE_ULTRALIGHT); - - // Check if this could actually be a MIFARE - NfcA a = NfcA.get(tag); - - mType = TYPE_UNKNOWN; - - if (a.getSak() == 0x00 && tag.getId()[0] == NXP_MANUFACTURER_ID) { - Bundle extras = tag.getTechExtras(TagTechnology.MIFARE_ULTRALIGHT); - if (extras.getBoolean(EXTRA_IS_UL_C)) { - mType = TYPE_ULTRALIGHT_C; - } else { - mType = TYPE_ULTRALIGHT; - } - } - } - - /** - * Return the MIFARE Ultralight type of the tag. - * <p>One of {@link #TYPE_ULTRALIGHT} or {@link #TYPE_ULTRALIGHT_C} or - * {@link #TYPE_UNKNOWN}. - * <p>Depending on how the tag has been formatted, it can be impossible - * to accurately classify between original MIFARE Ultralight and - * Ultralight C. So treat this method as a hint. - * <p>Does not cause any RF activity and does not block. - * - * @return the type - */ - public int getType() { - return mType; - } - - /** - * Read 4 pages (16 bytes). - * - * <p>The MIFARE Ultralight protocol always reads 4 pages at a time, to - * reduce the number of commands required to read an entire tag. - * <p>If a read spans past the last readable block, then the tag will - * return pages that have been wrapped back to the first blocks. MIFARE - * Ultralight tags have readable blocks 0x00 through 0x0F. So a read to - * block offset 0x0E would return blocks 0x0E, 0x0F, 0x00, 0x01. MIFARE - * Ultralight C tags have readable blocks 0x00 through 0x2B. So a read to - * block 0x2A would return blocks 0x2A, 0x2B, 0x00, 0x01. - * - * <p>This is an I/O operation and will block until complete. It must - * not be called from the main application thread. A blocked call will be canceled with - * {@link IOException} if {@link #close} is called from another thread. - * - * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. - * - * @param pageOffset index of first page to read, starting from 0 - * @return 4 pages (16 bytes) - * @throws TagLostException if the tag leaves the field - * @throws IOException if there is an I/O failure, or the operation is canceled - */ - public byte[] readPages(int pageOffset) throws IOException { - validatePageIndex(pageOffset); - checkConnected(); - - byte[] cmd = { 0x30, (byte) pageOffset}; - return transceive(cmd, false); - } - - /** - * Write 1 page (4 bytes). - * - * <p>The MIFARE Ultralight protocol always writes 1 page at a time, to - * minimize EEPROM write cycles. - * - * <p>This is an I/O operation and will block until complete. It must - * not be called from the main application thread. A blocked call will be canceled with - * {@link IOException} if {@link #close} is called from another thread. - * - * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. - * - * @param pageOffset index of page to write, starting from 0 - * @param data 4 bytes to write - * @throws TagLostException if the tag leaves the field - * @throws IOException if there is an I/O failure, or the operation is canceled - */ - public void writePage(int pageOffset, byte[] data) throws IOException { - validatePageIndex(pageOffset); - checkConnected(); - - byte[] cmd = new byte[data.length + 2]; - cmd[0] = (byte) 0xA2; - cmd[1] = (byte) pageOffset; - System.arraycopy(data, 0, cmd, 2, data.length); - - transceive(cmd, false); - } - - /** - * Send raw NfcA data to a tag and receive the response. - * - * <p>This is equivalent to connecting to this tag via {@link NfcA} - * and calling {@link NfcA#transceive}. Note that all MIFARE Classic - * tags are based on {@link NfcA} technology. - * - * <p>Use {@link #getMaxTransceiveLength} to retrieve the maximum number of bytes - * that can be sent with {@link #transceive}. - * - * <p>This is an I/O operation and will block until complete. It must - * not be called from the main application thread. A blocked call will be canceled with - * {@link IOException} if {@link #close} is called from another thread. - * - * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. - * - * @see NfcA#transceive - */ - public byte[] transceive(byte[] data) throws IOException { - return transceive(data, true); - } - - /** - * Return the maximum number of bytes that can be sent with {@link #transceive}. - * @return the maximum number of bytes that can be sent with {@link #transceive}. - */ - public int getMaxTransceiveLength() { - return getMaxTransceiveLengthInternal(); - } - - /** - * Set the {@link #transceive} timeout in milliseconds. - * - * <p>The timeout only applies to {@link #transceive} on this object, - * and is reset to a default value when {@link #close} is called. - * - * <p>Setting a longer timeout may be useful when performing - * transactions that require a long processing time on the tag - * such as key generation. - * - * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. - * - * @param timeout timeout value in milliseconds - * @throws SecurityException if the tag object is reused after the tag has left the field - */ - public void setTimeout(int timeout) { - try { - int err = mTag.getTagService().setTimeout( - TagTechnology.MIFARE_ULTRALIGHT, timeout); - if (err != ErrorCodes.SUCCESS) { - throw new IllegalArgumentException("The supplied timeout is not valid"); - } - } catch (RemoteException e) { - Log.e(TAG, "NFC service dead", e); - } - } - - /** - * Get the current {@link #transceive} timeout in milliseconds. - * - * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. - * - * @return timeout value in milliseconds - * @throws SecurityException if the tag object is reused after the tag has left the field - */ - public int getTimeout() { - try { - return mTag.getTagService().getTimeout(TagTechnology.MIFARE_ULTRALIGHT); - } catch (RemoteException e) { - Log.e(TAG, "NFC service dead", e); - return 0; - } - } - - private static void validatePageIndex(int pageIndex) { - // Do not be too strict on upper bounds checking, since some cards - // may have more addressable memory than they report. - // Note that issuing a command to an out-of-bounds block is safe - the - // tag will wrap the read to an addressable area. This validation is a - // helper to guard against obvious programming mistakes. - if (pageIndex < 0 || pageIndex >= MAX_PAGE_COUNT) { - throw new IndexOutOfBoundsException("page out of bounds: " + pageIndex); - } - } -} diff --git a/nfc/java/android/nfc/tech/Ndef.java b/nfc/java/android/nfc/tech/Ndef.java deleted file mode 100644 index 7d83f157a314..000000000000 --- a/nfc/java/android/nfc/tech/Ndef.java +++ /dev/null @@ -1,408 +0,0 @@ -/* - * Copyright (C) 2010 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.tech; - -import android.nfc.ErrorCodes; -import android.nfc.FormatException; -import android.nfc.INfcTag; -import android.nfc.NdefMessage; -import android.nfc.Tag; -import android.nfc.TagLostException; -import android.os.Bundle; -import android.os.RemoteException; -import android.util.Log; - -import java.io.IOException; - -/** - * Provides access to NDEF content and operations on a {@link Tag}. - * - * <p>Acquire a {@link Ndef} object using {@link #get}. - * - * <p>NDEF is an NFC Forum data format. The data formats are implemented in - * {@link android.nfc.NdefMessage} and - * {@link android.nfc.NdefRecord}. This class provides methods to - * retrieve and modify the {@link android.nfc.NdefMessage} - * on a tag. - * - * <p>There are currently four NFC Forum standardized tag types that can be - * formatted to contain NDEF data. - * <ul> - * <li>NFC Forum Type 1 Tag ({@link #NFC_FORUM_TYPE_1}), such as the Innovision Topaz - * <li>NFC Forum Type 2 Tag ({@link #NFC_FORUM_TYPE_2}), such as the NXP MIFARE Ultralight - * <li>NFC Forum Type 3 Tag ({@link #NFC_FORUM_TYPE_3}), such as Sony Felica - * <li>NFC Forum Type 4 Tag ({@link #NFC_FORUM_TYPE_4}), such as NXP MIFARE Desfire - * </ul> - * It is mandatory for all Android devices with NFC to correctly enumerate - * {@link Ndef} on NFC Forum Tag Types 1-4, and implement all NDEF operations - * as defined in this class. - * - * <p>Some vendors have their own well defined specifications for storing NDEF data - * on tags that do not fall into the above categories. Android devices with NFC - * should enumerate and implement {@link Ndef} under these vendor specifications - * where possible, but it is not mandatory. {@link #getType} returns a String - * describing this specification, for example {@link #MIFARE_CLASSIC} is - * <code>com.nxp.ndef.mifareclassic</code>. - * - * <p>Android devices that support MIFARE Classic must also correctly - * implement {@link Ndef} on MIFARE Classic tags formatted to NDEF. - * - * <p>For guaranteed compatibility across all Android devices with NFC, it is - * recommended to use NFC Forum Types 1-4 in new deployments of NFC tags - * with NDEF payload. Vendor NDEF formats will not work on all Android devices. - * - * <p class="note"><strong>Note:</strong> Methods that perform I/O operations - * require the {@link android.Manifest.permission#NFC} permission. - */ -public final class Ndef extends BasicTagTechnology { - private static final String TAG = "NFC"; - - /** @hide */ - public static final int NDEF_MODE_READ_ONLY = 1; - /** @hide */ - public static final int NDEF_MODE_READ_WRITE = 2; - /** @hide */ - public static final int NDEF_MODE_UNKNOWN = 3; - - /** @hide */ - public static final String EXTRA_NDEF_MSG = "ndefmsg"; - - /** @hide */ - public static final String EXTRA_NDEF_MAXLENGTH = "ndefmaxlength"; - - /** @hide */ - public static final String EXTRA_NDEF_CARDSTATE = "ndefcardstate"; - - /** @hide */ - public static final String EXTRA_NDEF_TYPE = "ndeftype"; - - /** @hide */ - public static final int TYPE_OTHER = -1; - /** @hide */ - public static final int TYPE_1 = 1; - /** @hide */ - public static final int TYPE_2 = 2; - /** @hide */ - public static final int TYPE_3 = 3; - /** @hide */ - public static final int TYPE_4 = 4; - /** @hide */ - public static final int TYPE_MIFARE_CLASSIC = 101; - /** @hide */ - public static final int TYPE_ICODE_SLI = 102; - - /** @hide */ - public static final String UNKNOWN = "android.ndef.unknown"; - - /** NFC Forum Tag Type 1 */ - public static final String NFC_FORUM_TYPE_1 = "org.nfcforum.ndef.type1"; - /** NFC Forum Tag Type 2 */ - public static final String NFC_FORUM_TYPE_2 = "org.nfcforum.ndef.type2"; - /** NFC Forum Tag Type 3 */ - public static final String NFC_FORUM_TYPE_3 = "org.nfcforum.ndef.type3"; - /** NFC Forum Tag Type 4 */ - public static final String NFC_FORUM_TYPE_4 = "org.nfcforum.ndef.type4"; - /** NDEF on MIFARE Classic */ - public static final String MIFARE_CLASSIC = "com.nxp.ndef.mifareclassic"; - /** - * NDEF on iCODE SLI - * @hide - */ - public static final String ICODE_SLI = "com.nxp.ndef.icodesli"; - - private final int mMaxNdefSize; - private final int mCardState; - private final NdefMessage mNdefMsg; - private final int mNdefType; - - /** - * Get an instance of {@link Ndef} for the given tag. - * - * <p>Returns null if {@link Ndef} was not enumerated in {@link Tag#getTechList}. - * This indicates the tag is not NDEF formatted, or that this tag - * is NDEF formatted but under a vendor specification that this Android - * device does not implement. - * - * <p>Does not cause any RF activity and does not block. - * - * @param tag an NDEF compatible tag - * @return Ndef object - */ - public static Ndef get(Tag tag) { - if (!tag.hasTech(TagTechnology.NDEF)) return null; - try { - return new Ndef(tag); - } catch (RemoteException e) { - return null; - } - } - - /** - * Internal constructor, to be used by NfcAdapter - * @hide - */ - public Ndef(Tag tag) throws RemoteException { - super(tag, TagTechnology.NDEF); - Bundle extras = tag.getTechExtras(TagTechnology.NDEF); - if (extras != null) { - mMaxNdefSize = extras.getInt(EXTRA_NDEF_MAXLENGTH); - mCardState = extras.getInt(EXTRA_NDEF_CARDSTATE); - mNdefMsg = extras.getParcelable(EXTRA_NDEF_MSG, android.nfc.NdefMessage.class); - mNdefType = extras.getInt(EXTRA_NDEF_TYPE); - } else { - throw new NullPointerException("NDEF tech extras are null."); - } - - } - - /** - * Get the {@link NdefMessage} that was read from the tag at discovery time. - * - * <p>If the NDEF Message is modified by an I/O operation then it - * will not be updated here, this function only returns what was discovered - * when the tag entered the field. - * <p>Note that this method may return null if the tag was in the - * INITIALIZED state as defined by NFC Forum, as in this state the - * tag is formatted to support NDEF but does not contain a message yet. - * <p>Does not cause any RF activity and does not block. - * @return NDEF Message read from the tag at discovery time, can be null - */ - public NdefMessage getCachedNdefMessage() { - return mNdefMsg; - } - - /** - * Get the NDEF tag type. - * - * <p>Returns one of {@link #NFC_FORUM_TYPE_1}, {@link #NFC_FORUM_TYPE_2}, - * {@link #NFC_FORUM_TYPE_3}, {@link #NFC_FORUM_TYPE_4}, - * {@link #MIFARE_CLASSIC} or another NDEF tag type that has not yet been - * formalized in this Android API. - * - * <p>Does not cause any RF activity and does not block. - * - * @return a string representing the NDEF tag type - */ - public String getType() { - switch (mNdefType) { - case TYPE_1: - return NFC_FORUM_TYPE_1; - case TYPE_2: - return NFC_FORUM_TYPE_2; - case TYPE_3: - return NFC_FORUM_TYPE_3; - case TYPE_4: - return NFC_FORUM_TYPE_4; - case TYPE_MIFARE_CLASSIC: - return MIFARE_CLASSIC; - case TYPE_ICODE_SLI: - return ICODE_SLI; - default: - return UNKNOWN; - } - } - - /** - * Get the maximum NDEF message size in bytes. - * - * <p>Does not cause any RF activity and does not block. - * - * @return size in bytes - */ - public int getMaxSize() { - return mMaxNdefSize; - } - - /** - * Determine if the tag is writable. - * - * <p>NFC Forum tags can be in read-only or read-write states. - * - * <p>Does not cause any RF activity and does not block. - * - * <p>Requires {@link android.Manifest.permission#NFC} permission. - * - * @return true if the tag is writable - */ - public boolean isWritable() { - return (mCardState == NDEF_MODE_READ_WRITE); - } - - /** - * Read the current {@link android.nfc.NdefMessage} on this tag. - * - * <p>This always reads the current NDEF Message stored on the tag. - * - * <p>Note that this method may return null if the tag was in the - * INITIALIZED state as defined by NFC Forum, as in that state the - * tag is formatted to support NDEF but does not contain a message yet. - * - * <p>This is an I/O operation and will block until complete. It must - * not be called from the main application thread. A blocked call will be canceled with - * {@link IOException} if {@link #close} is called from another thread. - * - * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. - * - * @return the NDEF Message, can be null - * @throws TagLostException if the tag leaves the field - * @throws IOException if there is an I/O failure, or the operation is canceled - * @throws FormatException if the NDEF Message on the tag is malformed - * @throws SecurityException if the tag object is reused after the tag has left the field - */ - public NdefMessage getNdefMessage() throws IOException, FormatException { - checkConnected(); - - try { - INfcTag tagService = mTag.getTagService(); - if (tagService == null) { - throw new IOException("Mock tags don't support this operation."); - } - int serviceHandle = mTag.getServiceHandle(); - if (tagService.isNdef(serviceHandle)) { - NdefMessage msg = tagService.ndefRead(serviceHandle); - if (msg == null && !tagService.isPresent(serviceHandle)) { - throw new TagLostException(); - } - return msg; - } else if (!tagService.isPresent(serviceHandle)) { - throw new TagLostException(); - } else { - return null; - } - } catch (RemoteException e) { - Log.e(TAG, "NFC service dead", e); - return null; - } - } - - /** - * Overwrite the {@link NdefMessage} on this tag. - * - * <p>This is an I/O operation and will block until complete. It must - * not be called from the main application thread. A blocked call will be canceled with - * {@link IOException} if {@link #close} is called from another thread. - * - * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. - * - * @param msg the NDEF Message to write, must not be null - * @throws TagLostException if the tag leaves the field - * @throws IOException if there is an I/O failure, or the operation is canceled - * @throws FormatException if the NDEF Message to write is malformed - * @throws SecurityException if the tag object is reused after the tag has left the field - */ - public void writeNdefMessage(NdefMessage msg) throws IOException, FormatException { - checkConnected(); - - try { - INfcTag tagService = mTag.getTagService(); - if (tagService == null) { - throw new IOException("Mock tags don't support this operation."); - } - int serviceHandle = mTag.getServiceHandle(); - if (tagService.isNdef(serviceHandle)) { - int errorCode = tagService.ndefWrite(serviceHandle, msg); - switch (errorCode) { - case ErrorCodes.SUCCESS: - break; - case ErrorCodes.ERROR_IO: - throw new IOException(); - case ErrorCodes.ERROR_INVALID_PARAM: - throw new FormatException(); - default: - // Should not happen - throw new IOException(); - } - } - else { - throw new IOException("Tag is not ndef"); - } - } catch (RemoteException e) { - Log.e(TAG, "NFC service dead", e); - } - } - - /** - * Indicates whether a tag can be made read-only with {@link #makeReadOnly()}. - * - * <p>Does not cause any RF activity and does not block. - * - * @return true if it is possible to make this tag read-only - * @throws SecurityException if the tag object is reused after the tag has left the field - */ - public boolean canMakeReadOnly() { - INfcTag tagService = mTag.getTagService(); - if (tagService == null) { - return false; - } - try { - return tagService.canMakeReadOnly(mNdefType); - } catch (RemoteException e) { - Log.e(TAG, "NFC service dead", e); - return false; - } - } - - /** - * Make a tag read-only. - * - * <p>This sets the CC field to indicate the tag is read-only, - * and where possible permanently sets the lock bits to prevent - * any further modification of the memory. - * <p>This is a one-way process and cannot be reverted! - * - * <p>This is an I/O operation and will block until complete. It must - * not be called from the main application thread. A blocked call will be canceled with - * {@link IOException} if {@link #close} is called from another thread. - * - * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. - * - * @return true on success, false if it is not possible to make this tag read-only - * @throws TagLostException if the tag leaves the field - * @throws IOException if there is an I/O failure, or the operation is canceled - * @throws SecurityException if the tag object is reused after the tag has left the field - */ - public boolean makeReadOnly() throws IOException { - checkConnected(); - - try { - INfcTag tagService = mTag.getTagService(); - if (tagService == null) { - return false; - } - if (tagService.isNdef(mTag.getServiceHandle())) { - int errorCode = tagService.ndefMakeReadOnly(mTag.getServiceHandle()); - switch (errorCode) { - case ErrorCodes.SUCCESS: - return true; - case ErrorCodes.ERROR_IO: - throw new IOException(); - case ErrorCodes.ERROR_INVALID_PARAM: - return false; - default: - // Should not happen - throw new IOException(); - } - } - else { - throw new IOException("Tag is not ndef"); - } - } catch (RemoteException e) { - Log.e(TAG, "NFC service dead", e); - return false; - } - } -} diff --git a/nfc/java/android/nfc/tech/NdefFormatable.java b/nfc/java/android/nfc/tech/NdefFormatable.java deleted file mode 100644 index 2240fe7f7d3b..000000000000 --- a/nfc/java/android/nfc/tech/NdefFormatable.java +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright (C) 2010 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.tech; - -import android.nfc.ErrorCodes; -import android.nfc.FormatException; -import android.nfc.INfcTag; -import android.nfc.NdefMessage; -import android.nfc.Tag; -import android.nfc.TagLostException; -import android.os.RemoteException; -import android.util.Log; - -import java.io.IOException; - -/** - * Provide access to NDEF format operations on a {@link Tag}. - * - * <p>Acquire a {@link NdefFormatable} object using {@link #get}. - * - * <p>Android devices with NFC must only enumerate and implement this - * class for tags for which it can format to NDEF. - * - * <p>Unfortunately the procedures to convert unformated tags to NDEF formatted - * tags are not specified by NFC Forum, and are not generally well-known. So - * there is no mandatory set of tags for which all Android devices with NFC - * must support {@link NdefFormatable}. - * - * <p class="note"><strong>Note:</strong> Methods that perform I/O operations - * require the {@link android.Manifest.permission#NFC} permission. - */ -public final class NdefFormatable extends BasicTagTechnology { - private static final String TAG = "NFC"; - - /** - * Get an instance of {@link NdefFormatable} for the given tag. - * <p>Does not cause any RF activity and does not block. - * <p>Returns null if {@link NdefFormatable} was not enumerated in {@link Tag#getTechList}. - * This indicates the tag is not NDEF formatable by this Android device. - * - * @param tag an NDEF formatable tag - * @return NDEF formatable object - */ - public static NdefFormatable get(Tag tag) { - if (!tag.hasTech(TagTechnology.NDEF_FORMATABLE)) return null; - try { - return new NdefFormatable(tag); - } catch (RemoteException e) { - return null; - } - } - - /** - * Internal constructor, to be used by NfcAdapter - * @hide - */ - public NdefFormatable(Tag tag) throws RemoteException { - super(tag, TagTechnology.NDEF_FORMATABLE); - } - - /** - * Format a tag as NDEF, and write a {@link NdefMessage}. - * - * <p>This is a multi-step process, an IOException is thrown - * if any one step fails. - * <p>The card is left in a read-write state after this operation. - * - * <p>This is an I/O operation and will block until complete. It must - * not be called from the main application thread. A blocked call will be canceled with - * {@link IOException} if {@link #close} is called from another thread. - * - * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. - * - * @param firstMessage the NDEF message to write after formatting, can be null - * @throws TagLostException if the tag leaves the field - * @throws IOException if there is an I/O failure, or the operation is canceled - * @throws FormatException if the NDEF Message to write is malformed - */ - public void format(NdefMessage firstMessage) throws IOException, FormatException { - format(firstMessage, false); - } - - /** - * Formats a tag as NDEF, write a {@link NdefMessage}, and make read-only. - * - * <p>This is a multi-step process, an IOException is thrown - * if any one step fails. - * <p>The card is left in a read-only state if this method returns successfully. - * - * <p>This is an I/O operation and will block until complete. It must - * not be called from the main application thread. A blocked call will be canceled with - * {@link IOException} if {@link #close} is called from another thread. - * - * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. - * - * @param firstMessage the NDEF message to write after formatting - * @throws TagLostException if the tag leaves the field - * @throws IOException if there is an I/O failure, or the operation is canceled - * @throws FormatException if the NDEF Message to write is malformed - * @throws SecurityException if the tag object is reused after the tag has left the field - */ - public void formatReadOnly(NdefMessage firstMessage) throws IOException, FormatException { - format(firstMessage, true); - } - - /*package*/ void format(NdefMessage firstMessage, boolean makeReadOnly) throws IOException, - FormatException { - checkConnected(); - - try { - int serviceHandle = mTag.getServiceHandle(); - INfcTag tagService = mTag.getTagService(); - if (tagService == null) { - throw new IOException(); - } - int errorCode = tagService.formatNdef(serviceHandle, MifareClassic.KEY_DEFAULT); - switch (errorCode) { - case ErrorCodes.SUCCESS: - break; - case ErrorCodes.ERROR_IO: - throw new IOException(); - case ErrorCodes.ERROR_INVALID_PARAM: - throw new FormatException(); - default: - // Should not happen - throw new IOException(); - } - // Now check and see if the format worked - if (!tagService.isNdef(serviceHandle)) { - throw new IOException(); - } - - // Write a message, if one was provided - if (firstMessage != null) { - errorCode = tagService.ndefWrite(serviceHandle, firstMessage); - switch (errorCode) { - case ErrorCodes.SUCCESS: - break; - case ErrorCodes.ERROR_IO: - throw new IOException(); - case ErrorCodes.ERROR_INVALID_PARAM: - throw new FormatException(); - default: - // Should not happen - throw new IOException(); - } - } - - // optionally make read-only - if (makeReadOnly) { - errorCode = tagService.ndefMakeReadOnly(serviceHandle); - switch (errorCode) { - case ErrorCodes.SUCCESS: - break; - case ErrorCodes.ERROR_IO: - throw new IOException(); - case ErrorCodes.ERROR_INVALID_PARAM: - throw new IOException(); - default: - // Should not happen - throw new IOException(); - } - } - } catch (RemoteException e) { - Log.e(TAG, "NFC service dead", e); - } - } -} diff --git a/nfc/java/android/nfc/tech/NfcA.java b/nfc/java/android/nfc/tech/NfcA.java deleted file mode 100644 index 7e6648361670..000000000000 --- a/nfc/java/android/nfc/tech/NfcA.java +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright (C) 2010 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.tech; - -import android.nfc.ErrorCodes; -import android.nfc.Tag; -import android.os.Bundle; -import android.os.RemoteException; -import android.util.Log; - -import java.io.IOException; - -/** - * Provides access to NFC-A (ISO 14443-3A) properties and I/O operations on a {@link Tag}. - * - * <p>Acquire a {@link NfcA} object using {@link #get}. - * <p>The primary NFC-A I/O operation is {@link #transceive}. Applications must - * implement their own protocol stack on top of {@link #transceive}. - * - * <p class="note"><strong>Note:</strong> Methods that perform I/O operations - * require the {@link android.Manifest.permission#NFC} permission. - */ -public final class NfcA extends BasicTagTechnology { - private static final String TAG = "NFC"; - - /** @hide */ - public static final String EXTRA_SAK = "sak"; - /** @hide */ - public static final String EXTRA_ATQA = "atqa"; - - private short mSak; - private byte[] mAtqa; - - /** - * Get an instance of {@link NfcA} for the given tag. - * <p>Returns null if {@link NfcA} was not enumerated in {@link Tag#getTechList}. - * This indicates the tag does not support NFC-A. - * <p>Does not cause any RF activity and does not block. - * - * @param tag an NFC-A compatible tag - * @return NFC-A object - */ - public static NfcA get(Tag tag) { - if (!tag.hasTech(TagTechnology.NFC_A)) return null; - try { - return new NfcA(tag); - } catch (RemoteException e) { - return null; - } - } - - /** @hide */ - public NfcA(Tag tag) throws RemoteException { - super(tag, TagTechnology.NFC_A); - Bundle extras = tag.getTechExtras(TagTechnology.NFC_A); - mSak = extras.getShort(EXTRA_SAK); - mAtqa = extras.getByteArray(EXTRA_ATQA); - } - - /** - * Return the ATQA/SENS_RES bytes from tag discovery. - * - * <p>Does not cause any RF activity and does not block. - * - * @return ATQA/SENS_RES bytes - */ - public byte[] getAtqa() { - return mAtqa; - } - - /** - * Return the SAK/SEL_RES bytes from tag discovery. - * - * <p>Does not cause any RF activity and does not block. - * - * @return SAK bytes - */ - public short getSak() { - return mSak; - } - - /** - * Send raw NFC-A commands to the tag and receive the response. - * - * <p>Applications must not append the EoD (CRC) to the payload, - * it will be automatically calculated. - * <p>Applications must only send commands that are complete bytes, - * for example a SENS_REQ is not possible (these are used to - * manage tag polling and initialization). - * - * <p>Use {@link #getMaxTransceiveLength} to retrieve the maximum number of bytes - * that can be sent with {@link #transceive}. - * - * <p>This is an I/O operation and will block until complete. It must - * not be called from the main application thread. A blocked call will be canceled with - * {@link IOException} if {@link #close} is called from another thread. - * - * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. - * - * @param data bytes to send - * @return bytes received in response - * @throws TagLostException if the tag leaves the field - * @throws IOException if there is an I/O failure, or this operation is canceled - */ - public byte[] transceive(byte[] data) throws IOException { - return transceive(data, true); - } - - /** - * Return the maximum number of bytes that can be sent with {@link #transceive}. - * @return the maximum number of bytes that can be sent with {@link #transceive}. - */ - public int getMaxTransceiveLength() { - return getMaxTransceiveLengthInternal(); - } - - /** - * Set the {@link #transceive} timeout in milliseconds. - * - * <p>The timeout only applies to {@link #transceive} on this object, - * and is reset to a default value when {@link #close} is called. - * - * <p>Setting a longer timeout may be useful when performing - * transactions that require a long processing time on the tag - * such as key generation. - * - * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. - * - * @param timeout timeout value in milliseconds - * @throws SecurityException if the tag object is reused after the tag has left the field - */ - public void setTimeout(int timeout) { - try { - int err = mTag.getTagService().setTimeout(TagTechnology.NFC_A, timeout); - if (err != ErrorCodes.SUCCESS) { - throw new IllegalArgumentException("The supplied timeout is not valid"); - } - } catch (RemoteException e) { - Log.e(TAG, "NFC service dead", e); - } - } - - /** - * Get the current {@link #transceive} timeout in milliseconds. - * - * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. - * - * @return timeout value in milliseconds - * @throws SecurityException if the tag object is reused after the tag has left the field - */ - public int getTimeout() { - try { - return mTag.getTagService().getTimeout(TagTechnology.NFC_A); - } catch (RemoteException e) { - Log.e(TAG, "NFC service dead", e); - return 0; - } - } -} diff --git a/nfc/java/android/nfc/tech/NfcB.java b/nfc/java/android/nfc/tech/NfcB.java deleted file mode 100644 index 3ebd47f610c0..000000000000 --- a/nfc/java/android/nfc/tech/NfcB.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (C) 2010 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.tech; - -import android.nfc.Tag; -import android.os.Bundle; -import android.os.RemoteException; - -import java.io.IOException; - -/** - * Provides access to NFC-B (ISO 14443-3B) properties and I/O operations on a {@link Tag}. - * - * <p>Acquire a {@link NfcB} object using {@link #get}. - * <p>The primary NFC-B I/O operation is {@link #transceive}. Applications must - * implement their own protocol stack on top of {@link #transceive}. - * - * <p class="note"><strong>Note:</strong> Methods that perform I/O operations - * require the {@link android.Manifest.permission#NFC} permission. - */ -public final class NfcB extends BasicTagTechnology { - /** @hide */ - public static final String EXTRA_APPDATA = "appdata"; - /** @hide */ - public static final String EXTRA_PROTINFO = "protinfo"; - - private byte[] mAppData; - private byte[] mProtInfo; - - /** - * Get an instance of {@link NfcB} for the given tag. - * <p>Returns null if {@link NfcB} was not enumerated in {@link Tag#getTechList}. - * This indicates the tag does not support NFC-B. - * <p>Does not cause any RF activity and does not block. - * - * @param tag an NFC-B compatible tag - * @return NFC-B object - */ - public static NfcB get(Tag tag) { - if (!tag.hasTech(TagTechnology.NFC_B)) return null; - try { - return new NfcB(tag); - } catch (RemoteException e) { - return null; - } - } - - /** @hide */ - public NfcB(Tag tag) throws RemoteException { - super(tag, TagTechnology.NFC_B); - Bundle extras = tag.getTechExtras(TagTechnology.NFC_B); - mAppData = extras.getByteArray(EXTRA_APPDATA); - mProtInfo = extras.getByteArray(EXTRA_PROTINFO); - } - - /** - * Return the Application Data bytes from ATQB/SENSB_RES at tag discovery. - * - * <p>Does not cause any RF activity and does not block. - * - * @return Application Data bytes from ATQB/SENSB_RES bytes - */ - public byte[] getApplicationData() { - return mAppData; - } - - /** - * Return the Protocol Info bytes from ATQB/SENSB_RES at tag discovery. - * - * <p>Does not cause any RF activity and does not block. - * - * @return Protocol Info bytes from ATQB/SENSB_RES bytes - */ - public byte[] getProtocolInfo() { - return mProtInfo; - } - - /** - * Send raw NFC-B commands to the tag and receive the response. - * - * <p>Applications must not append the EoD (CRC) to the payload, - * it will be automatically calculated. - * <p>Applications must not send commands that manage the polling - * loop and initialization (SENSB_REQ, SLOT_MARKER etc). - * - * <p>Use {@link #getMaxTransceiveLength} to retrieve the maximum number of bytes - * that can be sent with {@link #transceive}. - * - * <p>This is an I/O operation and will block until complete. It must - * not be called from the main application thread. A blocked call will be canceled with - * {@link IOException} if {@link #close} is called from another thread. - * - * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. - * - * @param data bytes to send - * @return bytes received in response - * @throws TagLostException if the tag leaves the field - * @throws IOException if there is an I/O failure, or this operation is canceled - */ - public byte[] transceive(byte[] data) throws IOException { - return transceive(data, true); - } - - /** - * Return the maximum number of bytes that can be sent with {@link #transceive}. - * @return the maximum number of bytes that can be sent with {@link #transceive}. - */ - public int getMaxTransceiveLength() { - return getMaxTransceiveLengthInternal(); - } -} diff --git a/nfc/java/android/nfc/tech/NfcBarcode.java b/nfc/java/android/nfc/tech/NfcBarcode.java deleted file mode 100644 index 421ba7827cb1..000000000000 --- a/nfc/java/android/nfc/tech/NfcBarcode.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (C) 2012 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.tech; - -import android.nfc.Tag; -import android.os.Bundle; -import android.os.RemoteException; - -/** - * Provides access to tags containing just a barcode. - * - * <p>Acquire an {@link NfcBarcode} object using {@link #get}. - * - */ -public final class NfcBarcode extends BasicTagTechnology { - - /** Kovio Tags */ - public static final int TYPE_KOVIO = 1; - public static final int TYPE_UNKNOWN = -1; - - /** @hide */ - public static final String EXTRA_BARCODE_TYPE = "barcodetype"; - - private int mType; - - /** - * Get an instance of {@link NfcBarcode} for the given tag. - * - * <p>Returns null if {@link NfcBarcode} was not enumerated in {@link Tag#getTechList}. - * - * <p>Does not cause any RF activity and does not block. - * - * @param tag an NfcBarcode compatible tag - * @return NfcBarcode object - */ - public static NfcBarcode get(Tag tag) { - if (!tag.hasTech(TagTechnology.NFC_BARCODE)) return null; - try { - return new NfcBarcode(tag); - } catch (RemoteException e) { - return null; - } - } - - /** - * Internal constructor, to be used by NfcAdapter - * @hide - */ - public NfcBarcode(Tag tag) throws RemoteException { - super(tag, TagTechnology.NFC_BARCODE); - Bundle extras = tag.getTechExtras(TagTechnology.NFC_BARCODE); - if (extras != null) { - mType = extras.getInt(EXTRA_BARCODE_TYPE); - } else { - throw new NullPointerException("NfcBarcode tech extras are null."); - } - } - - /** - * Returns the NFC Barcode tag type. - * - * <p>Currently only one of {@link #TYPE_KOVIO} or {@link #TYPE_UNKNOWN}. - * - * <p>Does not cause any RF activity and does not block. - * - * @return the NFC Barcode tag type - */ - public int getType() { - return mType; - } - - /** - * Returns the barcode of an NfcBarcode tag. - * - * <p> Tags of {@link #TYPE_KOVIO} return 16 bytes: - * <ul> - * <p> The first byte is 0x80 ORd with a manufacturer ID, corresponding - * to ISO/IEC 7816-6. - * <p> The second byte describes the payload data format. Defined data - * format types include the following:<ul> - * <li>0x00: Reserved for manufacturer assignment</li> - * <li>0x01: 96-bit URL with "http://www." prefix</li> - * <li>0x02: 96-bit URL with "https://www." prefix</li> - * <li>0x03: 96-bit URL with "http://" prefix</li> - * <li>0x04: 96-bit URL with "https://" prefix</li> - * <li>0x05: 96-bit GS1 EPC</li> - * <li>0x06-0xFF: reserved</li> - * </ul> - * <p>The following 12 bytes are payload:<ul> - * <li> In case of a URL payload, the payload is encoded in US-ASCII, - * following the limitations defined in RFC3987. - * {@see <a href="http://www.ietf.org/rfc/rfc3987.txt">RFC 3987</a>}</li> - * <li> In case of GS1 EPC data, see <a href="http://www.gs1.org/gsmp/kc/epcglobal/tds/"> - * GS1 Electronic Product Code (EPC) Tag Data Standard (TDS)</a> for more details. - * </li> - * </ul> - * <p>The last 2 bytes comprise the CRC. - * </ul> - * <p>Does not cause any RF activity and does not block. - * - * @return a byte array containing the barcode - * @see <a href="http://www.thinfilm.no/docs/thinfilm-nfc-barcode-datasheet.pdf"> - * Thinfilm NFC Barcode tag specification (previously Kovio NFC Barcode)</a> - * @see <a href="http://www.thinfilm.no/docs/thinfilm-nfc-barcode-data-format.pdf"> - * Thinfilm NFC Barcode data format (previously Kovio NFC Barcode)</a> - */ - public byte[] getBarcode() { - switch (mType) { - case TYPE_KOVIO: - // For Kovio tags the barcode matches the ID - return mTag.getId(); - default: - return null; - } - } -} diff --git a/nfc/java/android/nfc/tech/NfcF.java b/nfc/java/android/nfc/tech/NfcF.java deleted file mode 100644 index 2ccd38875f07..000000000000 --- a/nfc/java/android/nfc/tech/NfcF.java +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright (C) 2010 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.tech; - -import android.nfc.ErrorCodes; -import android.nfc.Tag; -import android.os.Bundle; -import android.os.RemoteException; -import android.util.Log; - -import java.io.IOException; - -/** - * Provides access to NFC-F (JIS 6319-4) properties and I/O operations on a {@link Tag}. - * - * <p>Acquire a {@link NfcF} object using {@link #get}. - * <p>The primary NFC-F I/O operation is {@link #transceive}. Applications must - * implement their own protocol stack on top of {@link #transceive}. - * - * <p class="note"><strong>Note:</strong> Methods that perform I/O operations - * require the {@link android.Manifest.permission#NFC} permission. - */ -public final class NfcF extends BasicTagTechnology { - private static final String TAG = "NFC"; - - /** @hide */ - public static final String EXTRA_SC = "systemcode"; - /** @hide */ - public static final String EXTRA_PMM = "pmm"; - - private byte[] mSystemCode = null; - private byte[] mManufacturer = null; - - /** - * Get an instance of {@link NfcF} for the given tag. - * <p>Returns null if {@link NfcF} was not enumerated in {@link Tag#getTechList}. - * This indicates the tag does not support NFC-F. - * <p>Does not cause any RF activity and does not block. - * - * @param tag an NFC-F compatible tag - * @return NFC-F object - */ - public static NfcF get(Tag tag) { - if (!tag.hasTech(TagTechnology.NFC_F)) return null; - try { - return new NfcF(tag); - } catch (RemoteException e) { - return null; - } - } - - /** @hide */ - public NfcF(Tag tag) throws RemoteException { - super(tag, TagTechnology.NFC_F); - Bundle extras = tag.getTechExtras(TagTechnology.NFC_F); - if (extras != null) { - mSystemCode = extras.getByteArray(EXTRA_SC); - mManufacturer = extras.getByteArray(EXTRA_PMM); - } - } - - /** - * Return the System Code bytes from tag discovery. - * - * <p>Does not cause any RF activity and does not block. - * - * @return System Code bytes - */ - public byte[] getSystemCode() { - return mSystemCode; - } - - /** - * Return the Manufacturer bytes from tag discovery. - * - * <p>Does not cause any RF activity and does not block. - * - * @return Manufacturer bytes - */ - public byte[] getManufacturer() { - return mManufacturer; - } - - /** - * Send raw NFC-F commands to the tag and receive the response. - * - * <p>Applications must not prefix the SoD (preamble and sync code) - * and/or append the EoD (CRC) to the payload, it will be automatically calculated. - * - * <p>A typical NFC-F frame for this method looks like: - * <pre> - * LENGTH (1 byte) --- CMD (1 byte) -- IDm (8 bytes) -- PARAMS (LENGTH - 10 bytes) - * </pre> - * - * <p>Use {@link #getMaxTransceiveLength} to retrieve the maximum amount of bytes - * that can be sent with {@link #transceive}. - * - * <p>This is an I/O operation and will block until complete. It must - * not be called from the main application thread. A blocked call will be canceled with - * {@link IOException} if {@link #close} is called from another thread. - * - * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. - * - * @param data bytes to send - * @return bytes received in response - * @throws TagLostException if the tag leaves the field - * @throws IOException if there is an I/O failure, or this operation is canceled - */ - public byte[] transceive(byte[] data) throws IOException { - return transceive(data, true); - } - - /** - * Return the maximum number of bytes that can be sent with {@link #transceive}. - * @return the maximum number of bytes that can be sent with {@link #transceive}. - */ - public int getMaxTransceiveLength() { - return getMaxTransceiveLengthInternal(); - } - - /** - * Set the {@link #transceive} timeout in milliseconds. - * - * <p>The timeout only applies to {@link #transceive} on this object, - * and is reset to a default value when {@link #close} is called. - * - * <p>Setting a longer timeout may be useful when performing - * transactions that require a long processing time on the tag - * such as key generation. - * - * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. - * - * @param timeout timeout value in milliseconds - * @throws SecurityException if the tag object is reused after the tag has left the field - */ - public void setTimeout(int timeout) { - try { - int err = mTag.getTagService().setTimeout(TagTechnology.NFC_F, timeout); - if (err != ErrorCodes.SUCCESS) { - throw new IllegalArgumentException("The supplied timeout is not valid"); - } - } catch (RemoteException e) { - Log.e(TAG, "NFC service dead", e); - } - } - - /** - * Get the current {@link #transceive} timeout in milliseconds. - * - * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. - * - * @return timeout value in milliseconds - * @throws SecurityException if the tag object is reused after the tag has left the field - */ - public int getTimeout() { - try { - return mTag.getTagService().getTimeout(TagTechnology.NFC_F); - } catch (RemoteException e) { - Log.e(TAG, "NFC service dead", e); - return 0; - } - } -} diff --git a/nfc/java/android/nfc/tech/NfcV.java b/nfc/java/android/nfc/tech/NfcV.java deleted file mode 100644 index 186c63bf07fd..000000000000 --- a/nfc/java/android/nfc/tech/NfcV.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (C) 2010 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.tech; - -import android.nfc.Tag; -import android.os.Bundle; -import android.os.RemoteException; - -import java.io.IOException; - -/** - * Provides access to NFC-V (ISO 15693) properties and I/O operations on a {@link Tag}. - * - * <p>Acquire a {@link NfcV} object using {@link #get}. - * <p>The primary NFC-V I/O operation is {@link #transceive}. Applications must - * implement their own protocol stack on top of {@link #transceive}. - * - * <p class="note"><strong>Note:</strong> Methods that perform I/O operations - * require the {@link android.Manifest.permission#NFC} permission. - */ -public final class NfcV extends BasicTagTechnology { - /** @hide */ - public static final String EXTRA_RESP_FLAGS = "respflags"; - - /** @hide */ - public static final String EXTRA_DSFID = "dsfid"; - - private byte mRespFlags; - private byte mDsfId; - - /** - * Get an instance of {@link NfcV} for the given tag. - * <p>Returns null if {@link NfcV} was not enumerated in {@link Tag#getTechList}. - * This indicates the tag does not support NFC-V. - * <p>Does not cause any RF activity and does not block. - * - * @param tag an NFC-V compatible tag - * @return NFC-V object - */ - public static NfcV get(Tag tag) { - if (!tag.hasTech(TagTechnology.NFC_V)) return null; - try { - return new NfcV(tag); - } catch (RemoteException e) { - return null; - } - } - - /** @hide */ - public NfcV(Tag tag) throws RemoteException { - super(tag, TagTechnology.NFC_V); - Bundle extras = tag.getTechExtras(TagTechnology.NFC_V); - mRespFlags = extras.getByte(EXTRA_RESP_FLAGS); - mDsfId = extras.getByte(EXTRA_DSFID); - } - - /** - * Return the Response Flag bytes from tag discovery. - * - * <p>Does not cause any RF activity and does not block. - * - * @return Response Flag bytes - */ - public byte getResponseFlags() { - return mRespFlags; - } - - /** - * Return the DSF ID bytes from tag discovery. - * - * <p>Does not cause any RF activity and does not block. - * - * @return DSF ID bytes - */ - public byte getDsfId() { - return mDsfId; - } - - /** - * Send raw NFC-V commands to the tag and receive the response. - * - * <p>Applications must not append the CRC to the payload, - * it will be automatically calculated. The application does - * provide FLAGS, CMD and PARAMETER bytes. - * - * <p>Use {@link #getMaxTransceiveLength} to retrieve the maximum amount of bytes - * that can be sent with {@link #transceive}. - * - * <p>This is an I/O operation and will block until complete. It must - * not be called from the main application thread. A blocked call will be canceled with - * {@link IOException} if {@link #close} is called from another thread. - * - * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. - * - * @param data bytes to send - * @return bytes received in response - * @throws TagLostException if the tag leaves the field - * @throws IOException if there is an I/O failure, or this operation is canceled - */ - public byte[] transceive(byte[] data) throws IOException { - return transceive(data, true); - } - - - /** - * Return the maximum number of bytes that can be sent with {@link #transceive}. - * @return the maximum number of bytes that can be sent with {@link #transceive}. - */ - public int getMaxTransceiveLength() { - return getMaxTransceiveLengthInternal(); - } -} diff --git a/nfc/java/android/nfc/tech/TagTechnology.java b/nfc/java/android/nfc/tech/TagTechnology.java deleted file mode 100644 index 839fe429b338..000000000000 --- a/nfc/java/android/nfc/tech/TagTechnology.java +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Copyright (C) 2010 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.tech; - -import android.nfc.Tag; - -import java.io.Closeable; -import java.io.IOException; - -/** - * {@link TagTechnology} is an interface to a technology in a {@link Tag}. - * <p> - * Obtain a {@link TagTechnology} implementation by calling the static method <code>get()</code> - * on the implementation class. - * <p> - * NFC tags are based on a number of independently developed technologies and offer a - * wide range of capabilities. The - * {@link TagTechnology} implementations provide access to these different - * technologies and capabilities. Some sub-classes map to technology - * specification (for example {@link NfcA}, {@link IsoDep}, others map to - * pseudo-technologies or capabilities (for example {@link Ndef}, {@link NdefFormatable}). - * <p> - * It is mandatory for all Android NFC devices to provide the following - * {@link TagTechnology} implementations. - * <ul> - * <li>{@link NfcA} (also known as ISO 14443-3A) - * <li>{@link NfcB} (also known as ISO 14443-3B) - * <li>{@link NfcF} (also known as JIS 6319-4) - * <li>{@link NfcV} (also known as ISO 15693) - * <li>{@link IsoDep} - * <li>{@link Ndef} on NFC Forum Type 1, Type 2, Type 3 or Type 4 compliant tags - * </ul> - * It is optional for Android NFC devices to provide the following - * {@link TagTechnology} implementations. If it is not provided, the - * Android device will never enumerate that class via {@link Tag#getTechList}. - * <ul> - * <li>{@link MifareClassic} - * <li>{@link MifareUltralight} - * <li>{@link NfcBarcode} - * <li>{@link NdefFormatable} must only be enumerated on tags for which this Android device - * is capable of formatting. Proprietary knowledge is often required to format a tag - * to make it NDEF compatible. - * </ul> - * <p> - * {@link TagTechnology} implementations provide methods that fall into two classes: - * <em>cached getters</em> and <em>I/O operations</em>. - * <h4>Cached getters</h4> - * These methods (usually prefixed by <code>get</code> or <code>is</code>) return - * properties of the tag, as determined at discovery time. These methods will never - * block or cause RF activity, and do not require {@link #connect} to have been called. - * They also never update, for example if a property is changed by an I/O operation with a tag - * then the cached getter will still return the result from tag discovery time. - * <h4>I/O operations</h4> - * I/O operations may require RF activity, and may block. They have the following semantics. - * <ul> - * <li>{@link #connect} must be called before using any other I/O operation. - * <li>{@link #close} must be called after completing I/O operations with a - * {@link TagTechnology}, and it will cancel all other blocked I/O operations on other threads - * (including {@link #connect} with {@link IOException}. - * <li>Only one {@link TagTechnology} can be connected at a time. Other calls to - * {@link #connect} will return {@link IOException}. - * <li>I/O operations may block, and should never be called on the main application - * thread. - * </ul> - * - * <p class="note"><strong>Note:</strong> Methods that perform I/O operations - * require the {@link android.Manifest.permission#NFC} permission. - */ -public interface TagTechnology extends Closeable { - /** - * This technology is an instance of {@link NfcA}. - * <p>Support for this technology type is mandatory. - * @hide - */ - public static final int NFC_A = 1; - - /** - * This technology is an instance of {@link NfcB}. - * <p>Support for this technology type is mandatory. - * @hide - */ - public static final int NFC_B = 2; - - /** - * This technology is an instance of {@link IsoDep}. - * <p>Support for this technology type is mandatory. - * @hide - */ - public static final int ISO_DEP = 3; - - /** - * This technology is an instance of {@link NfcF}. - * <p>Support for this technology type is mandatory. - * @hide - */ - public static final int NFC_F = 4; - - /** - * This technology is an instance of {@link NfcV}. - * <p>Support for this technology type is mandatory. - * @hide - */ - public static final int NFC_V = 5; - - /** - * This technology is an instance of {@link Ndef}. - * <p>Support for this technology type is mandatory. - * @hide - */ - public static final int NDEF = 6; - - /** - * This technology is an instance of {@link NdefFormatable}. - * <p>Support for this technology type is mandatory. - * @hide - */ - public static final int NDEF_FORMATABLE = 7; - - /** - * This technology is an instance of {@link MifareClassic}. - * <p>Support for this technology type is optional. If a stack doesn't support this technology - * type tags using it must still be discovered and present the lower level radio interface - * technologies in use. - * @hide - */ - public static final int MIFARE_CLASSIC = 8; - - /** - * This technology is an instance of {@link MifareUltralight}. - * <p>Support for this technology type is optional. If a stack doesn't support this technology - * type tags using it must still be discovered and present the lower level radio interface - * technologies in use. - * @hide - */ - public static final int MIFARE_ULTRALIGHT = 9; - - /** - * This technology is an instance of {@link NfcBarcode}. - * <p>Support for this technology type is optional. If a stack doesn't support this technology - * type tags using it must still be discovered and present the lower level radio interface - * technologies in use. - * @hide - */ - public static final int NFC_BARCODE = 10; - - /** - * Get the {@link Tag} object backing this {@link TagTechnology} object. - * @return the {@link Tag} backing this {@link TagTechnology} object. - */ - public Tag getTag(); - - /** - * Enable I/O operations to the tag from this {@link TagTechnology} object. - * <p>May cause RF activity and may block. Must not be called - * from the main application thread. A blocked call will be canceled with - * {@link IOException} by calling {@link #close} from another thread. - * <p>Only one {@link TagTechnology} object can be connected to a {@link Tag} at a time. - * <p>Applications must call {@link #close} when I/O operations are complete. - * - * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. - * - * @see #close() - * @throws TagLostException if the tag leaves the field - * @throws IOException if there is an I/O failure, or connect is canceled - * @throws SecurityException if the tag object is reused after the tag has left the field - */ - public void connect() throws IOException; - - /** - * Re-connect to the {@link Tag} associated with this connection. Reconnecting to a tag can be - * used to reset the state of the tag itself. - * - * <p>May cause RF activity and may block. Must not be called - * from the main application thread. A blocked call will be canceled with - * {@link IOException} by calling {@link #close} from another thread. - * - * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. - * - * @see #connect() - * @see #close() - * @throws TagLostException if the tag leaves the field - * @throws IOException if there is an I/O failure, or connect is canceled - * @throws SecurityException if the tag object is reused after the tag has left the field - * @hide - */ - public void reconnect() throws IOException; - - /** - * Disable I/O operations to the tag from this {@link TagTechnology} object, and release resources. - * <p>Also causes all blocked I/O operations on other thread to be canceled and - * return with {@link IOException}. - * - * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. - * - * @see #connect() - * @throws SecurityException if the tag object is reused after the tag has left the field - */ - public void close() throws IOException; - - /** - * Helper to indicate if I/O operations should be possible. - * - * <p>Returns true if {@link #connect} has completed, and {@link #close} has not been - * called, and the {@link Tag} is not known to be out of range. - * <p>Does not cause RF activity, and does not block. - * - * @return true if I/O operations should be possible - */ - public boolean isConnected(); -} diff --git a/nfc/java/android/nfc/tech/package.html b/nfc/java/android/nfc/tech/package.html deleted file mode 100644 index a99828f90c5b..000000000000 --- a/nfc/java/android/nfc/tech/package.html +++ /dev/null @@ -1,13 +0,0 @@ -<HTML> -<BODY> -<p> -These classes provide access to a tag technology's features, which vary by the type -of tag that is scanned. A scanned tag can support multiple technologies, and you can find -out what they are by calling {@link android.nfc.Tag#getTechList getTechList()}.</p> - -<p>For more information on dealing with tag technologies and handling the ones that you care about, see -<a href="{@docRoot}guide/topics/nfc/index.html#dispatch">The Tag Dispatch System</a>. -The {@link android.nfc.tech.TagTechnology} interface provides an overview of the -supported technologies.</p> -</BODY> -</HTML> diff --git a/nfc/lint-baseline.xml b/nfc/lint-baseline.xml deleted file mode 100644 index 67b496e0baf3..000000000000 --- a/nfc/lint-baseline.xml +++ /dev/null @@ -1,81 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01"> - - <issue - id="FlaggedApi" - message="Method `NfcOemExtension()` is a flagged API and should be inside an `if (Flags.nfcOemExtension())` check (or annotate the surrounding method `NfcAdapter` with `@FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) to transfer requirement to caller`)" - errorLine1=" mNfcOemExtension = new NfcOemExtension(mContext, this);" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/nfc/java/android/nfc/NfcAdapter.java" - line="909" - column="28"/> - </issue> - - <issue - id="FlaggedApi" - message="Field `FLAG_SET_DEFAULT_TECH` is a flagged API and should be inside an `if (Flags.nfcSetDefaultDiscTech())` check (or annotate the surrounding method `setDiscoveryTechnology` with `@FlaggedApi(Flags.FLAG_NFC_SET_DEFAULT_DISC_TECH) to transfer requirement to caller`)" - errorLine1=" && ((pollTechnology & FLAG_SET_DEFAULT_TECH) == FLAG_SET_DEFAULT_TECH" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/nfc/java/android/nfc/NfcAdapter.java" - line="1917" - column="39"/> - </issue> - - <issue - id="FlaggedApi" - message="Field `FLAG_SET_DEFAULT_TECH` is a flagged API and should be inside an `if (Flags.nfcSetDefaultDiscTech())` check (or annotate the surrounding method `setDiscoveryTechnology` with `@FlaggedApi(Flags.FLAG_NFC_SET_DEFAULT_DISC_TECH) to transfer requirement to caller`)" - errorLine1=" && ((pollTechnology & FLAG_SET_DEFAULT_TECH) == FLAG_SET_DEFAULT_TECH" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/nfc/java/android/nfc/NfcAdapter.java" - line="1917" - column="65"/> - </issue> - - <issue - id="FlaggedApi" - message="Field `FLAG_SET_DEFAULT_TECH` is a flagged API and should be inside an `if (Flags.nfcSetDefaultDiscTech())` check (or annotate the surrounding method `setDiscoveryTechnology` with `@FlaggedApi(Flags.FLAG_NFC_SET_DEFAULT_DISC_TECH) to transfer requirement to caller`)" - errorLine1=" || (listenTechnology & FLAG_SET_DEFAULT_TECH) == FLAG_SET_DEFAULT_TECH)) {" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/nfc/java/android/nfc/NfcAdapter.java" - line="1918" - column="40"/> - </issue> - - <issue - id="FlaggedApi" - message="Field `FLAG_SET_DEFAULT_TECH` is a flagged API and should be inside an `if (Flags.nfcSetDefaultDiscTech())` check (or annotate the surrounding method `setDiscoveryTechnology` with `@FlaggedApi(Flags.FLAG_NFC_SET_DEFAULT_DISC_TECH) to transfer requirement to caller`)" - errorLine1=" || (listenTechnology & FLAG_SET_DEFAULT_TECH) == FLAG_SET_DEFAULT_TECH)) {" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/nfc/java/android/nfc/NfcAdapter.java" - line="1918" - column="66"/> - </issue> - - <issue - id="FlaggedApi" - message="Method `onVendorNciResponse()` is a flagged API and should be inside an `if (Flags.nfcVendorCmd())` check (or annotate the surrounding method `onVendorResponseReceived` with `@FlaggedApi(Flags.FLAG_NFC_VENDOR_CMD) to transfer requirement to caller`)" - errorLine1=" executor.execute(() -> callback.onVendorNciResponse(gid, oid, payload));" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/nfc/java/android/nfc/NfcVendorNciCallbackListener.java" - line="88" - column="44"/> - </issue> - - <issue - id="FlaggedApi" - message="Method `onVendorNciNotification()` is a flagged API and should be inside an `if (Flags.nfcVendorCmd())` check (or annotate the surrounding method `onVendorNotificationReceived` with `@FlaggedApi(Flags.FLAG_NFC_VENDOR_CMD) to transfer requirement to caller`)" - errorLine1=" executor.execute(() -> callback.onVendorNciNotification(gid, oid, payload));" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/nfc/java/android/nfc/NfcVendorNciCallbackListener.java" - line="106" - column="44"/> - </issue> - -</issues> diff --git a/nfc/tests/Android.bp b/nfc/tests/Android.bp deleted file mode 100644 index 17fb810c626b..000000000000 --- a/nfc/tests/Android.bp +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2021 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 { - default_team: "trendy_team_fwk_nfc", - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_base_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_base_license"], -} - -android_test { - name: "NfcManagerTests", - static_libs: [ - "androidx.test.core", - "androidx.test.rules", - "androidx.test.runner", - "androidx.test.ext.junit", - "mockito-target-extended-minus-junit4", - "frameworks-base-testutils", - "truth", - "androidx.annotation_annotation", - "androidx.appcompat_appcompat", - "flag-junit", - "platform-test-annotations", - "testables", - ], - libs: [ - "androidx.annotation_annotation", - "unsupportedappusage", // for android.compat.annotation.UnsupportedAppUsage - "framework-permission-s.stubs.module_lib", - "framework-permission.stubs.module_lib", - "android.test.base.stubs.system", - "android.test.mock.stubs.system", - "android.test.runner.stubs.system", - "framework-nfc.impl", - ], - jni_libs: [ - // Required for ExtendedMockito - "libdexmakerjvmtiagent", - "libstaticjvmtiagent", - ], - srcs: [ - "src/**/*.java", - ":framework-nfc-updatable-sources", - ":framework-nfc-non-updatable-sources", - ], - platform_apis: true, - certificate: "platform", - test_suites: [ - "device-tests", - "mts-nfc", - ], - min_sdk_version: "35", // Should be 36 later. -} diff --git a/nfc/tests/AndroidManifest.xml b/nfc/tests/AndroidManifest.xml deleted file mode 100644 index 95646720d3d5..000000000000 --- a/nfc/tests/AndroidManifest.xml +++ /dev/null @@ -1,31 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright 2021 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. ---> - -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="android.nfc"> - - <application android:debuggable="true"> - <uses-library android:name="android.test.runner" /> - </application> - - <!-- This is a self-instrumenting test package. --> - <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" - android:targetPackage="android.nfc" - android:label="NFC Manager Tests"> - </instrumentation> - -</manifest> - diff --git a/nfc/tests/AndroidTest.xml b/nfc/tests/AndroidTest.xml deleted file mode 100644 index 490d6f5df197..000000000000 --- a/nfc/tests/AndroidTest.xml +++ /dev/null @@ -1,32 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright 2021 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. ---> -<configuration description="Config for NFC Manager test cases"> - <option name="test-suite-tag" value="apct"/> - <option name="test-suite-tag" value="apct-instrumentation"/> - <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> - <option name="cleanup-apks" value="true" /> - <option name="test-file-name" value="NfcManagerTests.apk" /> - </target_preparer> - - <option name="test-suite-tag" value="apct"/> - <option name="test-tag" value="NfcManagerTests"/> - - <test class="com.android.tradefed.testtype.AndroidJUnitTest" > - <option name="package" value="android.nfc" /> - <option name="hidden-api-checks" value="false"/> - <option name="runner" value="androidx.test.runner.AndroidJUnitRunner"/> - </test> -</configuration> diff --git a/nfc/tests/src/android/nfc/NdefMessageTest.java b/nfc/tests/src/android/nfc/NdefMessageTest.java deleted file mode 100644 index 9ca295da75c3..000000000000 --- a/nfc/tests/src/android/nfc/NdefMessageTest.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 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 static com.google.common.truth.Truth.assertThat; - -import androidx.test.ext.junit.runners.AndroidJUnit4; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -@RunWith(AndroidJUnit4.class) -public class NdefMessageTest { - private NdefMessage mNdefMessage; - private NdefRecord mNdefRecord; - - @Before - public void setUp() { - mNdefRecord = NdefRecord.createUri("http://www.example.com"); - mNdefMessage = new NdefMessage(mNdefRecord); - } - - @After - public void tearDown() { - } - - @Test - public void testGetRecords() { - NdefRecord[] records = mNdefMessage.getRecords(); - assertThat(records).isNotNull(); - assertThat(records).hasLength(1); - assertThat(records[0]).isEqualTo(mNdefRecord); - } - - @Test - public void testToByteArray() throws FormatException { - byte[] bytes = mNdefMessage.toByteArray(); - assertThat(bytes).isNotNull(); - assertThat(bytes.length).isGreaterThan(0); - NdefMessage ndefMessage = new NdefMessage(bytes); - assertThat(ndefMessage).isNotNull(); - } -} diff --git a/nfc/tests/src/android/nfc/NdefRecordTest.java b/nfc/tests/src/android/nfc/NdefRecordTest.java deleted file mode 100644 index 044c67448329..000000000000 --- a/nfc/tests/src/android/nfc/NdefRecordTest.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 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 static com.google.common.truth.Truth.assertThat; - -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.filters.SmallTest; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.util.Locale; - -@SmallTest -@RunWith(AndroidJUnit4.class) -public class NdefRecordTest { - - @Test - public void testNdefRecordConstructor() throws FormatException { - NdefRecord applicationRecord = NdefRecord - .createApplicationRecord("com.android.test"); - NdefRecord ndefRecord = new NdefRecord(applicationRecord.toByteArray()); - assertThat(ndefRecord).isNotNull(); - assertThat(ndefRecord.toByteArray().length).isGreaterThan(0); - assertThat(ndefRecord.getType()).isEqualTo("android.com:pkg".getBytes()); - assertThat(ndefRecord.getPayload()).isEqualTo("com.android.test".getBytes()); - } - - @Test - public void testCreateExternal() { - NdefRecord ndefRecord = NdefRecord.createExternal("test", - "android.com:pkg", "com.android.test".getBytes()); - assertThat(ndefRecord).isNotNull(); - assertThat(ndefRecord.getType()).isEqualTo("test:android.com:pkg".getBytes()); - assertThat(ndefRecord.getPayload()).isEqualTo("com.android.test".getBytes()); - } - - @Test - public void testCreateUri() { - NdefRecord ndefRecord = NdefRecord.createUri("http://www.example.com"); - assertThat(ndefRecord).isNotNull(); - assertThat(ndefRecord.getTnf()).isEqualTo(NdefRecord.TNF_WELL_KNOWN); - assertThat(ndefRecord.getType()).isEqualTo(NdefRecord.RTD_URI); - } - - @Test - public void testCreateMime() { - NdefRecord ndefRecord = NdefRecord.createMime("text/plain", "example".getBytes()); - assertThat(ndefRecord).isNotNull(); - assertThat(ndefRecord.getTnf()).isEqualTo(NdefRecord.TNF_MIME_MEDIA); - } - - @Test - public void testCreateTextRecord() { - String languageCode = Locale.getDefault().getLanguage(); - NdefRecord ndefRecord = NdefRecord.createTextRecord(languageCode, "testdata"); - assertThat(ndefRecord).isNotNull(); - assertThat(ndefRecord.getTnf()).isEqualTo(NdefRecord.TNF_WELL_KNOWN); - assertThat(ndefRecord.getType()).isEqualTo(NdefRecord.RTD_TEXT); - } - -} diff --git a/nfc/tests/src/android/nfc/NfcAntennaInfoTest.java b/nfc/tests/src/android/nfc/NfcAntennaInfoTest.java deleted file mode 100644 index c24816d85517..000000000000 --- a/nfc/tests/src/android/nfc/NfcAntennaInfoTest.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 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 static com.google.common.truth.Truth.assertThat; - -import static org.mockito.Mockito.mock; - -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.filters.SmallTest; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.util.ArrayList; -import java.util.List; - -@SmallTest -@RunWith(AndroidJUnit4.class) -public class NfcAntennaInfoTest { - private NfcAntennaInfo mNfcAntennaInfo; - - - @Before - public void setUp() { - AvailableNfcAntenna availableNfcAntenna = mock(AvailableNfcAntenna.class); - List<AvailableNfcAntenna> antennas = new ArrayList<>(); - antennas.add(availableNfcAntenna); - mNfcAntennaInfo = new NfcAntennaInfo(1, 1, false, antennas); - } - - @After - public void tearDown() { - } - - @Test - public void testGetDeviceHeight() { - int height = mNfcAntennaInfo.getDeviceHeight(); - assertThat(height).isEqualTo(1); - } - - @Test - public void testGetDeviceWidth() { - int width = mNfcAntennaInfo.getDeviceWidth(); - assertThat(width).isEqualTo(1); - } - - @Test - public void testIsDeviceFoldable() { - boolean foldable = mNfcAntennaInfo.isDeviceFoldable(); - assertThat(foldable).isFalse(); - } - - @Test - public void testGetAvailableNfcAntennas() { - List<AvailableNfcAntenna> antennas = mNfcAntennaInfo.getAvailableNfcAntennas(); - assertThat(antennas).isNotNull(); - assertThat(antennas.size()).isEqualTo(1); - } - -} diff --git a/nfc/tests/src/android/nfc/NfcControllerAlwaysOnListenerTest.java b/nfc/tests/src/android/nfc/NfcControllerAlwaysOnListenerTest.java deleted file mode 100644 index 48f4288d401e..000000000000 --- a/nfc/tests/src/android/nfc/NfcControllerAlwaysOnListenerTest.java +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright 2021 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 static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -import android.nfc.NfcAdapter.ControllerAlwaysOnListener; -import android.os.RemoteException; - -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.filters.SmallTest; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.Executor; - -/** - * Test of {@link NfcControllerAlwaysOnListener}. - */ -@SmallTest -@RunWith(AndroidJUnit4.class) -public class NfcControllerAlwaysOnListenerTest { - - private INfcAdapter mNfcAdapter = mock(INfcAdapter.class); - - private Throwable mThrowRemoteException = new RemoteException("RemoteException"); - - private static Executor getExecutor() { - return new Executor() { - @Override - public void execute(Runnable command) { - command.run(); - } - }; - } - - private static void verifyListenerInvoked(ControllerAlwaysOnListener listener) { - verify(listener, times(1)).onControllerAlwaysOnChanged(anyBoolean()); - } - - @Test - public void testRegister_RegisterUnregisterWhenNotSupported() throws RemoteException { - // isControllerAlwaysOnSupported() returns false, not supported. - doReturn(false).when(mNfcAdapter).isControllerAlwaysOnSupported(); - NfcControllerAlwaysOnListener mListener = - new NfcControllerAlwaysOnListener(mNfcAdapter); - ControllerAlwaysOnListener mockListener1 = mock(ControllerAlwaysOnListener.class); - ControllerAlwaysOnListener mockListener2 = mock(ControllerAlwaysOnListener.class); - - // Verify that the state listener will not registered with the NFC Adapter - mListener.register(getExecutor(), mockListener1); - verify(mNfcAdapter, times(0)).registerControllerAlwaysOnListener(any()); - - // Register a second client and no any call to NFC Adapter - mListener.register(getExecutor(), mockListener2); - verify(mNfcAdapter, times(0)).registerControllerAlwaysOnListener(any()); - - // Unregister first listener, and no any call to NFC Adapter - mListener.unregister(mockListener1); - verify(mNfcAdapter, times(0)).registerControllerAlwaysOnListener(any()); - verify(mNfcAdapter, times(0)).unregisterControllerAlwaysOnListener(any()); - - // Unregister second listener, and no any call to NFC Adapter - mListener.unregister(mockListener2); - verify(mNfcAdapter, times(0)).registerControllerAlwaysOnListener(any()); - verify(mNfcAdapter, times(0)).unregisterControllerAlwaysOnListener(any()); - } - - @Test - public void testRegister_RegisterUnregister() throws RemoteException { - doReturn(true).when(mNfcAdapter).isControllerAlwaysOnSupported(); - NfcControllerAlwaysOnListener mListener = - new NfcControllerAlwaysOnListener(mNfcAdapter); - ControllerAlwaysOnListener mockListener1 = mock(ControllerAlwaysOnListener.class); - ControllerAlwaysOnListener mockListener2 = mock(ControllerAlwaysOnListener.class); - - // Verify that the state listener registered with the NFC Adapter - mListener.register(getExecutor(), mockListener1); - verify(mNfcAdapter, times(1)).registerControllerAlwaysOnListener(any()); - - // Register a second client and no new call to NFC Adapter - mListener.register(getExecutor(), mockListener2); - verify(mNfcAdapter, times(1)).registerControllerAlwaysOnListener(any()); - - // Unregister first listener - mListener.unregister(mockListener1); - verify(mNfcAdapter, times(1)).registerControllerAlwaysOnListener(any()); - verify(mNfcAdapter, times(0)).unregisterControllerAlwaysOnListener(any()); - - // Unregister second listener and the state listener registered with the NFC Adapter - mListener.unregister(mockListener2); - verify(mNfcAdapter, times(1)).registerControllerAlwaysOnListener(any()); - verify(mNfcAdapter, times(1)).unregisterControllerAlwaysOnListener(any()); - } - - @Test - public void testRegister_FirstRegisterFails() throws RemoteException { - doReturn(true).when(mNfcAdapter).isControllerAlwaysOnSupported(); - NfcControllerAlwaysOnListener mListener = - new NfcControllerAlwaysOnListener(mNfcAdapter); - ControllerAlwaysOnListener mockListener1 = mock(ControllerAlwaysOnListener.class); - ControllerAlwaysOnListener mockListener2 = mock(ControllerAlwaysOnListener.class); - - // Throw a remote exception whenever first registering - doThrow(mThrowRemoteException).when(mNfcAdapter).registerControllerAlwaysOnListener( - any()); - - mListener.register(getExecutor(), mockListener1); - verify(mNfcAdapter, times(1)).registerControllerAlwaysOnListener(any()); - - // No longer throw an exception, instead succeed - doNothing().when(mNfcAdapter).registerControllerAlwaysOnListener(any()); - - // Register a different listener - mListener.register(getExecutor(), mockListener2); - verify(mNfcAdapter, times(2)).registerControllerAlwaysOnListener(any()); - - // Ensure first and second listener were invoked - mListener.onControllerAlwaysOnChanged(true); - verifyListenerInvoked(mockListener1); - verifyListenerInvoked(mockListener2); - } - - @Test - public void testRegister_RegisterSameListenerTwice() throws RemoteException { - doReturn(true).when(mNfcAdapter).isControllerAlwaysOnSupported(); - NfcControllerAlwaysOnListener mListener = - new NfcControllerAlwaysOnListener(mNfcAdapter); - ControllerAlwaysOnListener mockListener = mock(ControllerAlwaysOnListener.class); - - // Register the same listener Twice - mListener.register(getExecutor(), mockListener); - mListener.register(getExecutor(), mockListener); - verify(mNfcAdapter, times(1)).registerControllerAlwaysOnListener(any()); - - // Invoke a state change and ensure the listener is only called once - mListener.onControllerAlwaysOnChanged(true); - verifyListenerInvoked(mockListener); - } - - @Test - public void testNotify_AllListenersNotified() throws RemoteException { - doReturn(true).when(mNfcAdapter).isControllerAlwaysOnSupported(); - NfcControllerAlwaysOnListener listener = new NfcControllerAlwaysOnListener(mNfcAdapter); - List<ControllerAlwaysOnListener> mockListeners = new ArrayList<>(); - for (int i = 0; i < 10; i++) { - ControllerAlwaysOnListener mockListener = mock(ControllerAlwaysOnListener.class); - listener.register(getExecutor(), mockListener); - mockListeners.add(mockListener); - } - - // Invoke a state change and ensure all listeners are invoked - listener.onControllerAlwaysOnChanged(true); - for (ControllerAlwaysOnListener mListener : mockListeners) { - verifyListenerInvoked(mListener); - } - } - - @Test - public void testStateChange_CorrectValue() throws RemoteException { - doReturn(true).when(mNfcAdapter).isControllerAlwaysOnSupported(); - runStateChangeValue(true, true); - runStateChangeValue(false, false); - - } - - private void runStateChangeValue(boolean isEnabledIn, boolean isEnabledOut) { - NfcControllerAlwaysOnListener listener = new NfcControllerAlwaysOnListener(mNfcAdapter); - ControllerAlwaysOnListener mockListener = mock(ControllerAlwaysOnListener.class); - listener.register(getExecutor(), mockListener); - listener.onControllerAlwaysOnChanged(isEnabledIn); - verify(mockListener, times(1)).onControllerAlwaysOnChanged(isEnabledOut); - verify(mockListener, times(0)).onControllerAlwaysOnChanged(!isEnabledOut); - } -} diff --git a/nfc/tests/src/android/nfc/NfcManagerTest.java b/nfc/tests/src/android/nfc/NfcManagerTest.java deleted file mode 100644 index 06314cc03d37..000000000000 --- a/nfc/tests/src/android/nfc/NfcManagerTest.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 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 static com.google.common.truth.Truth.assertThat; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import android.content.Context; - -import androidx.test.ext.junit.runners.AndroidJUnit4; - -import com.android.dx.mockito.inline.extended.ExtendedMockito; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.mockito.MockitoSession; -import org.mockito.quality.Strictness; - -@RunWith(AndroidJUnit4.class) -public class NfcManagerTest { - - private MockitoSession mMockitoSession; - private NfcManager mNfcManager; - @Mock - private Context mContext; - - @Before - public void setUp() { - mMockitoSession = ExtendedMockito.mockitoSession() - .mockStatic(NfcAdapter.class) - .strictness(Strictness.LENIENT) - .startMocking(); - MockitoAnnotations.initMocks(this); - - when(NfcAdapter.getNfcAdapter(any())).thenReturn(mock(NfcAdapter.class)); - when(mContext.getApplicationContext()).thenReturn(mContext); - mNfcManager = new NfcManager(mContext); - } - - @After - public void tearDown() { - mMockitoSession.finishMocking(); - } - - @Test - public void testGetDefaultAdapter() { - NfcAdapter nfcAdapter = mNfcManager.getDefaultAdapter(); - assertThat(nfcAdapter).isNotNull(); - } -} diff --git a/nfc/tests/src/android/nfc/NfcRoutingTableEntryTest.java b/nfc/tests/src/android/nfc/NfcRoutingTableEntryTest.java deleted file mode 100644 index a90a716b6081..000000000000 --- a/nfc/tests/src/android/nfc/NfcRoutingTableEntryTest.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 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 static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -@RunWith(JUnit4.class) -public final class NfcRoutingTableEntryTest { - - @Test - public void testAidEntry_GetAid() { - String expectedAid = "A00000061A02"; - RoutingTableAidEntry entry = new RoutingTableAidEntry(1, expectedAid, 0); - - assertEquals(expectedAid, entry.getAid()); - } - - @Test - public void testProtocolEntry_GetProtocol() { - RoutingTableProtocolEntry entry = - new RoutingTableProtocolEntry(1, RoutingTableProtocolEntry.PROTOCOL_T1T, 0); - - assertEquals(RoutingTableProtocolEntry.PROTOCOL_T1T, entry.getProtocol()); - } - - @Test - public void testSystemCodeEntry_GetSystemCode() { - byte[] expectedSystemCode = {0x01, 0x02, 0x03}; - RoutingTableSystemCodeEntry entry = - new RoutingTableSystemCodeEntry(1, expectedSystemCode, 0); - - assertArrayEquals(expectedSystemCode, entry.getSystemCode()); - } - - @Test - public void testTechnologyEntry_GetTechnology_A() { - RoutingTableTechnologyEntry entry = - new RoutingTableTechnologyEntry(1, RoutingTableTechnologyEntry.TECHNOLOGY_A, 0); - - assertEquals(RoutingTableTechnologyEntry.TECHNOLOGY_A, entry.getTechnology()); - } -} diff --git a/nfc/tests/src/android/nfc/OemLogItemsTest.java b/nfc/tests/src/android/nfc/OemLogItemsTest.java deleted file mode 100644 index 21ef80485cc4..000000000000 --- a/nfc/tests/src/android/nfc/OemLogItemsTest.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (C) 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 static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.mock; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -import java.time.Instant; - -@RunWith(JUnit4.class) -public final class OemLogItemsTest { - - @Test - public void testGetAction() { - OemLogItems item = new OemLogItems.Builder(OemLogItems.LOG_ACTION_RF_FIELD_STATE_CHANGED) - .build(); - assertEquals(OemLogItems.LOG_ACTION_RF_FIELD_STATE_CHANGED, item.getAction()); - } - - @Test - public void testGetEvent() { - OemLogItems item = new OemLogItems.Builder(OemLogItems.LOG_ACTION_NFC_TOGGLE) - .setCallingEvent(OemLogItems.EVENT_ENABLE) - .build(); - assertEquals(OemLogItems.EVENT_ENABLE, item.getEvent()); - } - - @Test - public void testGetCallingPid() { - OemLogItems item = new OemLogItems.Builder(OemLogItems.LOG_ACTION_NFC_TOGGLE) - .setCallingPid(1234) - .build(); - assertEquals(1234, item.getCallingPid()); - } - - @Test - public void testGetCommandApdu() { - byte[] commandApdu = {0x01, 0x02, 0x03}; - OemLogItems item = new OemLogItems.Builder(OemLogItems.LOG_ACTION_HCE_DATA) - .setApduCommand(commandApdu) - .build(); - assertArrayEquals(commandApdu, item.getCommandApdu()); - } - - @Test - public void testGetResponseApdu() { - byte[] responseApdu = {0x04, 0x05, 0x06}; - OemLogItems item = new OemLogItems.Builder(OemLogItems.LOG_ACTION_HCE_DATA) - .setApduResponse(responseApdu) - .build(); - assertArrayEquals(responseApdu, item.getResponseApdu()); - } - - @Test - public void testGetRfFieldEventTimeMillis() { - Instant expectedTime = Instant.ofEpochSecond(1688768000, 123456789); - OemLogItems item = new OemLogItems.Builder(OemLogItems.LOG_ACTION_RF_FIELD_STATE_CHANGED) - .setRfFieldOnTime(expectedTime) - .build(); - assertEquals(expectedTime, item.getRfFieldEventTimeMillis()); - } - - @Test - public void testGetTag() { - Tag mockTag = mock(Tag.class); - OemLogItems item = new OemLogItems.Builder(OemLogItems.LOG_ACTION_TAG_DETECTED) - .setTag(mockTag) - .build(); - assertEquals(mockTag, item.getTag()); - } -} diff --git a/nfc/tests/src/android/nfc/TechListParcelTest.java b/nfc/tests/src/android/nfc/TechListParcelTest.java deleted file mode 100644 index a12bbbc6884b..000000000000 --- a/nfc/tests/src/android/nfc/TechListParcelTest.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2022 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 static com.google.common.truth.Truth.assertThat; - -import android.os.Parcel; - -import androidx.test.ext.junit.runners.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.util.Arrays; - -@RunWith(AndroidJUnit4.class) -public class TechListParcelTest { - - private static final String[] TECH_LIST_1 = new String[] { "tech1.1", "tech1.2" }; - private static final String[] TECH_LIST_2 = new String[] { "tech2.1" }; - private static final String[] TECH_LIST_EMPTY = new String[] {}; - - @Test - public void testWriteParcel() { - TechListParcel techListParcel = new TechListParcel(TECH_LIST_1, TECH_LIST_2); - - Parcel parcel = Parcel.obtain(); - techListParcel.writeToParcel(parcel, 0); - parcel.setDataPosition(0); - TechListParcel actualTechList = - TechListParcel.CREATOR.createFromParcel(parcel); - parcel.recycle(); - - assertThat(actualTechList.getTechLists().length).isEqualTo(2); - assertThat(Arrays.equals(actualTechList.getTechLists()[0], TECH_LIST_1)).isTrue(); - assertThat(Arrays.equals(actualTechList.getTechLists()[1], TECH_LIST_2)).isTrue(); - } - - @Test - public void testWriteParcelArrayEmpty() { - TechListParcel techListParcel = new TechListParcel(); - - Parcel parcel = Parcel.obtain(); - techListParcel.writeToParcel(parcel, 0); - parcel.setDataPosition(0); - TechListParcel actualTechList = - TechListParcel.CREATOR.createFromParcel(parcel); - parcel.recycle(); - - assertThat(actualTechList.getTechLists().length).isEqualTo(0); - } - - @Test - public void testWriteParcelElementEmpty() { - TechListParcel techListParcel = new TechListParcel(TECH_LIST_EMPTY); - - Parcel parcel = Parcel.obtain(); - techListParcel.writeToParcel(parcel, 0); - parcel.setDataPosition(0); - TechListParcel actualTechList = - TechListParcel.CREATOR.createFromParcel(parcel); - parcel.recycle(); - - assertThat(actualTechList.getTechLists().length).isEqualTo(1); - assertThat(Arrays.equals(actualTechList.getTechLists()[0], TECH_LIST_EMPTY)).isTrue(); - } - -} diff --git a/nfc/tests/src/android/nfc/cardemulation/AidGroupTest.java b/nfc/tests/src/android/nfc/cardemulation/AidGroupTest.java deleted file mode 100644 index 7e0010247ee7..000000000000 --- a/nfc/tests/src/android/nfc/cardemulation/AidGroupTest.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (C) 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.cardemulation; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.isNull; -import static org.mockito.Mockito.atLeastOnce; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - -import android.os.Parcel; - -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.filters.SmallTest; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.xmlpull.v1.XmlSerializer; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -@SmallTest -@RunWith(AndroidJUnit4.class) -public class AidGroupTest { - private AidGroup mAidGroup; - - @Before - public void setUp() { - List<String> aids = new ArrayList<>(); - aids.add("A0000000031010"); - aids.add("A0000000041010"); - aids.add("A0000000034710"); - aids.add("A000000300"); - mAidGroup = new AidGroup(aids, "payment"); - } - - @After - public void tearDown() { - } - - @Test - public void testGetCategory() { - String category = mAidGroup.getCategory(); - assertThat(category).isNotNull(); - assertThat(category).isEqualTo("payment"); - } - - @Test - public void testGetAids() { - List<String> aids = mAidGroup.getAids(); - assertThat(aids).isNotNull(); - assertThat(aids.size()).isGreaterThan(0); - assertThat(aids.get(0)).isEqualTo("A0000000031010"); - } - - @Test - public void testWriteAsXml() throws IOException { - XmlSerializer out = mock(XmlSerializer.class); - mAidGroup.writeAsXml(out); - verify(out, atLeastOnce()).startTag(isNull(), anyString()); - verify(out, atLeastOnce()).attribute(isNull(), anyString(), anyString()); - verify(out, atLeastOnce()).endTag(isNull(), anyString()); - } - - @Test - public void testRightToParcel() { - Parcel parcel = mock(Parcel.class); - mAidGroup.writeToParcel(parcel, 0); - verify(parcel).writeString8(anyString()); - verify(parcel).writeInt(anyInt()); - verify(parcel).writeStringList(any()); - } -} diff --git a/nfc/tests/src/android/nfc/cardemulation/CardemulationTest.java b/nfc/tests/src/android/nfc/cardemulation/CardemulationTest.java deleted file mode 100644 index 48632064621c..000000000000 --- a/nfc/tests/src/android/nfc/cardemulation/CardemulationTest.java +++ /dev/null @@ -1,284 +0,0 @@ -/* - * Copyright (C) 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.cardemulation; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.app.role.RoleManager; -import android.content.ComponentName; -import android.content.ContentResolver; -import android.content.Context; -import android.content.pm.PackageManager; -import android.nfc.Constants; -import android.nfc.INfcCardEmulation; -import android.nfc.NfcAdapter; -import android.os.RemoteException; -import android.os.UserHandle; -import android.provider.Settings; - -import androidx.test.ext.junit.runners.AndroidJUnit4; - -import com.android.dx.mockito.inline.extended.ExtendedMockito; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.mockito.MockitoSession; -import org.mockito.quality.Strictness; - -import java.util.ArrayList; -import java.util.List; - -@RunWith(AndroidJUnit4.class) -public class CardemulationTest { - - private CardEmulation mCardEmulation; - @Mock - private Context mContext; - @Mock - private INfcCardEmulation mINfcCardEmulation; - @Mock - private NfcAdapter mNfcAdapter; - @Mock - private PackageManager mPackageManager; - private MockitoSession mMockitoSession; - - @Before - public void setUp() { - mMockitoSession = ExtendedMockito.mockitoSession() - .mockStatic(NfcAdapter.class) - .mockStatic(Settings.Secure.class) - .strictness(Strictness.LENIENT) - .startMocking(); - MockitoAnnotations.initMocks(this); - - when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) - .thenReturn(true); - when(mContext.getApplicationContext()).thenReturn(mContext); - when(mContext.getPackageManager()).thenReturn(mPackageManager); - assertThat(mNfcAdapter).isNotNull(); - when(mNfcAdapter.getCardEmulationService()).thenReturn(mINfcCardEmulation); - when(mNfcAdapter.getContext()).thenReturn(mContext); - mCardEmulation = CardEmulation.getInstance(mNfcAdapter); - } - - @After - public void tearDown() { - mMockitoSession.finishMocking(); - } - - @Test - public void testIsDefaultServiceForCategory() throws RemoteException { - ComponentName componentName = mock(ComponentName.class); - UserHandle userHandle = mock(UserHandle.class); - when(userHandle.getIdentifier()).thenReturn(1); - when(mContext.getUser()).thenReturn(userHandle); - when(mINfcCardEmulation.isDefaultServiceForCategory(1, componentName, - "payment")).thenReturn(true); - boolean result = mCardEmulation.isDefaultServiceForCategory(componentName, - "payment"); - assertThat(result).isTrue(); - verify(mINfcCardEmulation).isDefaultServiceForCategory(1, componentName, - "payment"); - - } - - @Test - public void testIsDefaultServiceForAid() throws RemoteException { - ComponentName componentName = mock(ComponentName.class); - UserHandle userHandle = mock(UserHandle.class); - when(userHandle.getIdentifier()).thenReturn(1); - when(mContext.getUser()).thenReturn(userHandle); - when(mINfcCardEmulation.isDefaultServiceForAid(1, componentName, - "payment")).thenReturn(true); - boolean result = mCardEmulation.isDefaultServiceForAid(componentName, - "payment"); - assertThat(result).isTrue(); - verify(mINfcCardEmulation).isDefaultServiceForAid(1, componentName, - "payment"); - } - - @Test - public void testCategoryAllowsForegroundPreference() throws Settings.SettingNotFoundException { - when(mContext.createContextAsUser(any(), anyInt())).thenReturn(mContext); - RoleManager roleManager = mock(RoleManager.class); - when(roleManager.isRoleAvailable(RoleManager.ROLE_WALLET)).thenReturn(false); - when(mContext.getSystemService(RoleManager.class)).thenReturn(roleManager); - ContentResolver contentResolver = mock(ContentResolver.class); - when(mContext.getContentResolver()).thenReturn(contentResolver); - when(Settings.Secure.getInt(contentResolver, Constants - .SETTINGS_SECURE_NFC_PAYMENT_FOREGROUND)).thenReturn(1); - boolean result = mCardEmulation.categoryAllowsForegroundPreference("payment"); - assertThat(result).isTrue(); - } - - @Test - public void testGetSelectionModeForCategory() throws RemoteException { - when(mINfcCardEmulation.isDefaultPaymentRegistered()).thenReturn(true); - int result = mCardEmulation.getSelectionModeForCategory("payment"); - assertThat(result).isEqualTo(0); - } - - @Test - public void testSetShouldDefaultToObserveModeForService() throws RemoteException { - UserHandle userHandle = mock(UserHandle.class); - when(userHandle.getIdentifier()).thenReturn(1); - when(mContext.getUser()).thenReturn(userHandle); - ComponentName componentName = mock(ComponentName.class); - when(mINfcCardEmulation.setShouldDefaultToObserveModeForService(1, componentName, true)) - .thenReturn(true); - boolean result = mCardEmulation - .setShouldDefaultToObserveModeForService(componentName, true); - assertThat(result).isTrue(); - verify(mINfcCardEmulation).setShouldDefaultToObserveModeForService(1, componentName, true); - } - - @Test - public void testRegisterPollingLoopFilterForService()throws RemoteException { - UserHandle userHandle = mock(UserHandle.class); - when(userHandle.getIdentifier()).thenReturn(1); - when(mContext.getUser()).thenReturn(userHandle); - ComponentName componentName = mock(ComponentName.class); - when(mINfcCardEmulation.registerPollingLoopFilterForService(anyInt(), - any(), anyString(), anyBoolean())).thenReturn(true); - boolean result = mCardEmulation.registerPollingLoopFilterForService(componentName, - "A0000000041010", true); - assertThat(result).isTrue(); - verify(mINfcCardEmulation) - .registerPollingLoopFilterForService(anyInt(), any(), anyString(), anyBoolean()); - } - - @Test - public void testRemovePollingLoopFilterForService() throws RemoteException { - UserHandle userHandle = mock(UserHandle.class); - when(userHandle.getIdentifier()).thenReturn(1); - when(mContext.getUser()).thenReturn(userHandle); - ComponentName componentName = mock(ComponentName.class); - when(mINfcCardEmulation.removePollingLoopFilterForService(anyInt(), any(), anyString())) - .thenReturn(true); - boolean result = mCardEmulation - .removePollingLoopFilterForService(componentName, "A0000000041010"); - assertThat(result).isTrue(); - verify(mINfcCardEmulation).removePollingLoopFilterForService(anyInt(), any(), anyString()); - } - - @Test - public void testRegisterPollingLoopPatternFilterForService() throws RemoteException { - UserHandle userHandle = mock(UserHandle.class); - when(userHandle.getIdentifier()).thenReturn(1); - when(mContext.getUser()).thenReturn(userHandle); - ComponentName componentName = mock(ComponentName.class); - when(mINfcCardEmulation.registerPollingLoopPatternFilterForService(anyInt(), any(), - anyString(), anyBoolean())).thenReturn(true); - boolean result = mCardEmulation.registerPollingLoopPatternFilterForService(componentName, - "A0000000041010", true); - assertThat(result).isTrue(); - verify(mINfcCardEmulation).registerPollingLoopPatternFilterForService(anyInt(), any(), - anyString(), anyBoolean()); - } - - @Test - public void testRemovePollingLoopPatternFilterForService() throws RemoteException { - UserHandle userHandle = mock(UserHandle.class); - when(userHandle.getIdentifier()).thenReturn(1); - when(mContext.getUser()).thenReturn(userHandle); - ComponentName componentName = mock(ComponentName.class); - when(mINfcCardEmulation.removePollingLoopPatternFilterForService(anyInt(), any(), - anyString())).thenReturn(true); - boolean result = mCardEmulation.removePollingLoopPatternFilterForService(componentName, - "A0000000041010"); - assertThat(result).isTrue(); - verify(mINfcCardEmulation).removePollingLoopPatternFilterForService(anyInt(), any(), - anyString()); - } - - @Test - public void testRegisterAidsForService() throws RemoteException { - UserHandle userHandle = mock(UserHandle.class); - when(userHandle.getIdentifier()).thenReturn(1); - when(mContext.getUser()).thenReturn(userHandle); - ComponentName componentName = mock(ComponentName.class); - when(mINfcCardEmulation.registerAidGroupForService(anyInt(), any(), - any())).thenReturn(true); - List<String> aids = new ArrayList<>(); - aids.add("A0000000041010"); - boolean result = mCardEmulation.registerAidsForService(componentName, "payment", - aids); - assertThat(result).isTrue(); - verify(mINfcCardEmulation).registerAidGroupForService(anyInt(), any(), - any()); - } - - @Test - public void testUnsetOffHostForService() throws RemoteException { - UserHandle userHandle = mock(UserHandle.class); - when(userHandle.getIdentifier()).thenReturn(1); - when(mContext.getUser()).thenReturn(userHandle); - ComponentName componentName = mock(ComponentName.class); - when(mINfcCardEmulation.unsetOffHostForService(1, componentName)).thenReturn(true); - boolean result = mCardEmulation.unsetOffHostForService(componentName); - assertThat(result).isTrue(); - verify(mINfcCardEmulation).unsetOffHostForService(1, componentName); - } - - @Test - public void testSetOffHostForService() throws RemoteException { - UserHandle userHandle = mock(UserHandle.class); - when(userHandle.getIdentifier()).thenReturn(1); - when(mContext.getUser()).thenReturn(userHandle); - when(NfcAdapter.getDefaultAdapter(any())).thenReturn(mNfcAdapter); - List<String> elements = new ArrayList<>(); - elements.add("eSE"); - when(mNfcAdapter.getSupportedOffHostSecureElements()).thenReturn(elements); - ComponentName componentName = mock(ComponentName.class); - when(mINfcCardEmulation.setOffHostForService(anyInt(), any(), anyString())) - .thenReturn(true); - boolean result = mCardEmulation.setOffHostForService(componentName, - "eSE"); - assertThat(result).isTrue(); - verify(mINfcCardEmulation).setOffHostForService(anyInt(), any(), anyString()); - } - - @Test - public void testGetAidsForService() throws RemoteException { - UserHandle userHandle = mock(UserHandle.class); - when(userHandle.getIdentifier()).thenReturn(1); - when(mContext.getUser()).thenReturn(userHandle); - ComponentName componentName = mock(ComponentName.class); - List<String> elements = new ArrayList<>(); - elements.add("eSE"); - AidGroup aidGroup = mock(AidGroup.class); - when(aidGroup.getAids()).thenReturn(elements); - when(mINfcCardEmulation.getAidGroupForService(1, componentName, "payment")) - .thenReturn(aidGroup); - List<String> result = mCardEmulation.getAidsForService(componentName, "payment"); - assertThat(result).isNotNull(); - assertThat(result.size()).isGreaterThan(0); - verify(mINfcCardEmulation).getAidGroupForService(1, componentName, "payment"); - } -} diff --git a/nfc/tests/src/android/nfc/dta/NfcDtaTest.java b/nfc/tests/src/android/nfc/dta/NfcDtaTest.java deleted file mode 100644 index 38fb7ef311b5..000000000000 --- a/nfc/tests/src/android/nfc/dta/NfcDtaTest.java +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright (C) 2017 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.dta; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.content.Context; -import android.nfc.INfcDta; -import android.nfc.NfcAdapter; -import android.os.RemoteException; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -public class NfcDtaTest { - private final String mServiceName = "serviceName"; - private final int mServiceSap = 1; - private final int mMiu = 1; - private final int mRwSize = 1; - private final int mTestCaseId = 1; - @Mock - private NfcAdapter mMockNfcAdapter; - @Mock - private INfcDta mMockService; - @Mock - private Context mMockContext; - - private NfcDta mNfcDta; - - @Before - public void setup() { - MockitoAnnotations.initMocks(this); - when(mMockNfcAdapter.getContext()).thenReturn(mMockContext); - when(mMockNfcAdapter.getNfcDtaInterface()).thenReturn(mMockService); - - mNfcDta = NfcDta.getInstance(mMockNfcAdapter); - } - - @Test - public void testEnableData() throws RemoteException { - assertTrue(mNfcDta.enableDta()); - verify(mMockService).enableDta(); - } - - @Test - public void testEnableDataWithRemoteException() throws RemoteException { - doThrow(new RemoteException()).when(mMockService).enableDta(); - - assertFalse(mNfcDta.enableDta()); - verify(mMockService).enableDta(); - } - - @Test - public void testDisableData() throws RemoteException { - assertTrue(mNfcDta.disableDta()); - verify(mMockService).disableDta(); - } - - @Test - public void testDisableDataWithRemoteException() throws RemoteException { - doThrow(new RemoteException()).when(mMockService).disableDta(); - - assertFalse(mNfcDta.disableDta()); - verify(mMockService).disableDta(); - } - - @Test - public void testEnableServer() throws RemoteException { - when(mMockService.enableServer(mServiceName, mServiceSap, mMiu, mRwSize, - mTestCaseId)).thenReturn(true); - - mNfcDta.enableServer(mServiceName, mServiceSap, mMiu, mRwSize, mTestCaseId); - verify(mMockService).enableServer(mServiceName, mServiceSap, mMiu, mRwSize, mTestCaseId); - } - - @Test - public void testEnableServerWithRemoteException() throws RemoteException { - doThrow(new RemoteException()).when(mMockService).enableServer(mServiceName, mServiceSap, - mMiu, - mRwSize, mTestCaseId); - - mNfcDta.enableServer(mServiceName, mServiceSap, mMiu, mRwSize, mTestCaseId); - verify(mMockService).enableServer(mServiceName, mServiceSap, mMiu, mRwSize, mTestCaseId); - } - - @Test - public void testDisableServer() throws RemoteException { - assertTrue(mNfcDta.disableServer()); - verify(mMockService).disableServer(); - } - - @Test - public void testDisableServerWithRemoteException() throws RemoteException { - doThrow(new RemoteException()).when(mMockService).disableServer(); - - assertFalse(mNfcDta.disableServer()); - verify(mMockService).disableServer(); - } - - @Test - public void testEnableClient() throws RemoteException { - when(mMockService.enableClient(mServiceName, mMiu, mRwSize, mTestCaseId)).thenReturn(true); - - mNfcDta.enableClient(mServiceName, mMiu, mRwSize, mTestCaseId); - verify(mMockService).enableClient(mServiceName, mMiu, mRwSize, mTestCaseId); - } - - @Test - public void testEnableClientWithRemoteException() throws RemoteException { - doThrow(new RemoteException()).when(mMockService).enableClient(mServiceName, mMiu, mRwSize, - mTestCaseId); - - mNfcDta.enableClient(mServiceName, mMiu, mRwSize, mTestCaseId); - verify(mMockService).enableClient(mServiceName, mMiu, mRwSize, mTestCaseId); - } - - @Test - public void testDisableClient() throws RemoteException { - assertTrue(mNfcDta.disableClient()); - verify(mMockService).disableClient(); - } - - @Test - public void testDisableClientWithRemoteException() throws RemoteException { - doThrow(new RemoteException()).when(mMockService).disableClient(); - - assertFalse(mNfcDta.disableClient()); - verify(mMockService).disableClient(); - } - - @Test - public void testRegisterMessageService() throws RemoteException { - String msgServiceName = "sampleServiceName"; - when(mMockService.registerMessageService(msgServiceName)).thenReturn(true); - - mNfcDta.registerMessageService(msgServiceName); - verify(mMockService).registerMessageService(msgServiceName); - } - - @Test - public void testRegisterMessageServiceWithRemoteException() throws RemoteException { - String msgServiceName = "sampleServiceName"; - doThrow(new RemoteException()).when(mMockService).registerMessageService(msgServiceName); - - assertFalse(mNfcDta.registerMessageService(msgServiceName)); - } - - @Test(expected = NullPointerException.class) - public void testGetInstanceWithNullPointerException() { - NfcDta.getInstance(null); - } - - @Test(expected = UnsupportedOperationException.class) - public void testGetInstanceWithUnsupportedOperationExceptionForNfcAdapterContext() { - when(mMockNfcAdapter.getContext()).thenReturn(null); - - NfcDta.getInstance(mMockNfcAdapter); - } -} diff --git a/nfc/tests/src/android/nfc/tech/NfcATest.java b/nfc/tests/src/android/nfc/tech/NfcATest.java deleted file mode 100644 index 40076ebd0a0a..000000000000 --- a/nfc/tests/src/android/nfc/tech/NfcATest.java +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright (C) 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.tech; - -import static android.nfc.tech.NfcA.EXTRA_ATQA; -import static android.nfc.tech.NfcA.EXTRA_SAK; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThrows; -import static org.junit.Assert.fail; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.nfc.ErrorCodes; -import android.nfc.INfcTag; -import android.nfc.Tag; -import android.nfc.TransceiveResult; -import android.os.Bundle; -import android.os.RemoteException; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.io.IOException; - -public class NfcATest { - @Mock - private Tag mMockTag; - @Mock - private INfcTag mMockTagService; - @Mock - private Bundle mMockBundle; - private NfcA mNfcA; - private final byte[] mSampleArray = new byte[] {1, 2, 3}; - - @Before - public void setUp() throws RemoteException { - MockitoAnnotations.initMocks(this); - when(mMockBundle.getShort(EXTRA_SAK)).thenReturn((short) 1); - when(mMockBundle.getByteArray(EXTRA_ATQA)).thenReturn(mSampleArray); - when(mMockTag.getTechExtras(TagTechnology.NFC_A)).thenReturn(mMockBundle); - - mNfcA = new NfcA(mMockTag); - } - - @Test - public void testGetNfcAWithTech() { - Tag mockTag = mock(Tag.class); - when(mockTag.getTechExtras(TagTechnology.NFC_A)).thenReturn(mMockBundle); - when(mockTag.hasTech(TagTechnology.NFC_A)).thenReturn(true); - - assertNotNull(NfcA.get(mockTag)); - verify(mockTag).getTechExtras(TagTechnology.NFC_A); - verify(mockTag).hasTech(TagTechnology.NFC_A); - } - - @Test - public void testGetNfcAWithoutTech() { - when(mMockTag.hasTech(TagTechnology.NFC_A)).thenReturn(false); - assertNull(NfcA.get(mMockTag)); - } - - @Test - public void testGetAtga() { - assertNotNull(mNfcA.getAtqa()); - } - - @Test - public void testGetSak() { - assertEquals((short) 1, mNfcA.getSak()); - } - - @Test - public void testTransceive() throws IOException, RemoteException { - TransceiveResult mockTransceiveResult = mock(TransceiveResult.class); - when(mMockTag.getConnectedTechnology()).thenReturn(TagTechnology.NFC_A); - when(mMockTag.getTagService()).thenReturn(mMockTagService); - when(mMockTag.getServiceHandle()).thenReturn(1); - when(mMockTagService.transceive(1, mSampleArray, true)) - .thenReturn(mockTransceiveResult); - when(mockTransceiveResult.getResponseOrThrow()).thenReturn(mSampleArray); - - mNfcA.transceive(mSampleArray); - verify(mMockTag).getTagService(); - verify(mMockTag).getServiceHandle(); - } - - @Test - public void testGetMaxTransceiveLength() throws RemoteException { - when(mMockTag.getTagService()).thenReturn(mMockTagService); - when(mMockTagService.getMaxTransceiveLength(TagTechnology.NFC_A)).thenReturn(1); - - mNfcA.getMaxTransceiveLength(); - verify(mMockTag).getTagService(); - } - - @Test - public void testSetTimeout() { - when(mMockTag.getTagService()).thenReturn(mMockTagService); - try { - when(mMockTagService.setTimeout(TagTechnology.NFC_A, 1000)).thenReturn( - ErrorCodes.SUCCESS); - - mNfcA.setTimeout(1000); - verify(mMockTag).getTagService(); - verify(mMockTagService).setTimeout(TagTechnology.NFC_A, 1000); - } catch (Exception e) { - fail("Unexpected exception during valid setTimeout: " + e.getMessage()); - } - } - - @Test - public void testSetTimeoutInvalidTimeout() { - when(mMockTag.getTagService()).thenReturn(mMockTagService); - try { - when(mMockTagService.setTimeout(TagTechnology.NFC_A, -1)).thenReturn( - ErrorCodes.ERROR_TIMEOUT); - - assertThrows(IllegalArgumentException.class, () -> mNfcA.setTimeout(-1)); - } catch (Exception e) { - fail("Unexpected exception during invalid setTimeout: " + e.getMessage()); - } - } - - @Test - public void testSetTimeoutRemoteException() { - when(mMockTag.getTagService()).thenReturn(mMockTagService); - try { - when(mMockTagService.setTimeout(TagTechnology.NFC_A, 1000)).thenThrow( - new RemoteException()); - - mNfcA.setTimeout(1000); // Should not throw an exception but log it - verify(mMockTag).getTagService(); - verify(mMockTagService).setTimeout(TagTechnology.NFC_A, 1000); - } catch (Exception e) { - fail("Unexpected exception during RemoteException in setTimeout: " + e.getMessage()); - } - - } - - @Test - public void testGetTimeout() { - when(mMockTag.getTagService()).thenReturn(mMockTagService); - try { - when(mMockTagService.getTimeout(TagTechnology.NFC_A)).thenReturn(2000); - - assertEquals(2000, mNfcA.getTimeout()); - verify(mMockTag).getTagService(); - verify(mMockTagService).getTimeout(TagTechnology.NFC_A); - } catch (Exception e) { - fail("Unexpected exception during valid getTimeout: " + e.getMessage()); - } - } - - @Test - public void testGetTimeoutRemoteException() { - when(mMockTag.getTagService()).thenReturn(mMockTagService); - try { - when(mMockTagService.getTimeout(TagTechnology.NFC_A)).thenThrow(new RemoteException()); - - assertEquals(0, mNfcA.getTimeout()); - } catch (Exception e) { - fail("Unexpected exception during RemoteException in getTimeout: " + e.getMessage()); - } - } -} diff --git a/nfc/tests/src/android/nfc/tech/NfcBTest.java b/nfc/tests/src/android/nfc/tech/NfcBTest.java deleted file mode 100644 index 98d6070e760c..000000000000 --- a/nfc/tests/src/android/nfc/tech/NfcBTest.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (C) 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.tech; - -import static android.nfc.tech.NfcB.EXTRA_APPDATA; -import static android.nfc.tech.NfcB.EXTRA_PROTINFO; - -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.nfc.INfcTag; -import android.nfc.Tag; -import android.nfc.TransceiveResult; -import android.os.Bundle; -import android.os.RemoteException; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.io.IOException; - -public class NfcBTest { - private final byte[] mSampleAppDate = new byte[] {1, 2, 3}; - private final byte[] mSampleProtInfo = new byte[] {3, 2, 1}; - @Mock - private Tag mMockTag; - @Mock - private Bundle mMockBundle; - @Mock - private INfcTag mMockTagService; - private NfcB mNfcB; - - @Before - public void setUp() throws RemoteException { - MockitoAnnotations.initMocks(this); - when(mMockBundle.getByteArray(EXTRA_APPDATA)).thenReturn(mSampleAppDate); - when(mMockBundle.getByteArray(EXTRA_PROTINFO)).thenReturn(mSampleProtInfo); - when(mMockTag.getTechExtras(TagTechnology.NFC_B)).thenReturn(mMockBundle); - - mNfcB = new NfcB(mMockTag); - } - - @Test - public void testGetApplicationData() { - assertNotNull(mNfcB.getApplicationData()); - } - - @Test - public void testGetProtocolInfo() { - assertNotNull(mNfcB.getProtocolInfo()); - } - - @Test - public void testGetNfcBInstance() { - Tag tag = mock(Tag.class); - when(tag.hasTech(TagTechnology.NFC_B)).thenReturn(true); - when(tag.getTechExtras(TagTechnology.NFC_B)).thenReturn(mMockBundle); - - assertNotNull(NfcB.get(tag)); - verify(tag).hasTech(TagTechnology.NFC_B); - verify(tag).getTechExtras(TagTechnology.NFC_B); - } - - @Test - public void testGetNfcBNullInstance() { - Tag tag = mock(Tag.class); - when(tag.hasTech(TagTechnology.NFC_B)).thenReturn(false); - - assertNull(NfcB.get(tag)); - verify(tag).hasTech(TagTechnology.NFC_B); - verify(tag, never()).getTechExtras(TagTechnology.NFC_B); - } - - - @Test - public void testTransceive() throws IOException, RemoteException { - byte[] sampleData = new byte[] {1, 2, 3, 4, 5}; - TransceiveResult mockTransceiveResult = mock(TransceiveResult.class); - when(mMockTag.getConnectedTechnology()).thenReturn(TagTechnology.NFC_B); - when(mMockTag.getTagService()).thenReturn(mMockTagService); - when(mMockTag.getServiceHandle()).thenReturn(1); - when(mMockTagService.transceive(1, sampleData, true)) - .thenReturn(mockTransceiveResult); - when(mockTransceiveResult.getResponseOrThrow()).thenReturn(sampleData); - - mNfcB.transceive(sampleData); - verify(mMockTag).getTagService(); - verify(mMockTag).getServiceHandle(); - } - - @Test - public void testGetMaxTransceiveLength() throws RemoteException { - when(mMockTag.getTagService()).thenReturn(mMockTagService); - when(mMockTagService.getMaxTransceiveLength(TagTechnology.NFC_B)).thenReturn(1); - - mNfcB.getMaxTransceiveLength(); - verify(mMockTag).getTagService(); - } -} diff --git a/nfc/tests/src/android/nfc/tech/NfcBarcodeTest.java b/nfc/tests/src/android/nfc/tech/NfcBarcodeTest.java deleted file mode 100644 index 3aa4e2c13895..000000000000 --- a/nfc/tests/src/android/nfc/tech/NfcBarcodeTest.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2012 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.tech; - -import static android.nfc.tech.NfcBarcode.EXTRA_BARCODE_TYPE; -import static android.nfc.tech.NfcBarcode.TYPE_KOVIO; -import static android.nfc.tech.NfcBarcode.TYPE_UNKNOWN; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.nfc.Tag; -import android.os.Bundle; -import android.os.RemoteException; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -public class NfcBarcodeTest { - @Mock - private Tag mMockTag; - @Mock - private Bundle mMockBundle; - private NfcBarcode mNfcBarcode; - - @Before - public void setUp() throws RemoteException { - MockitoAnnotations.initMocks(this); - when(mMockBundle.getInt(EXTRA_BARCODE_TYPE)).thenReturn(TYPE_KOVIO); - when(mMockTag.getTechExtras(TagTechnology.NFC_BARCODE)).thenReturn(mMockBundle); - - mNfcBarcode = new NfcBarcode(mMockTag); - } - - @Test - public void testGetNfcBarcodeInstance() { - Tag mockTag = mock(Tag.class); - when(mockTag.hasTech(TagTechnology.NFC_BARCODE)).thenReturn(true); - when(mockTag.getTechExtras(TagTechnology.NFC_BARCODE)).thenReturn(mMockBundle); - - assertNotNull(NfcBarcode.get(mockTag)); - verify(mockTag).hasTech(TagTechnology.NFC_BARCODE); - verify(mockTag).getTechExtras(TagTechnology.NFC_BARCODE); - } - - @Test(expected = NullPointerException.class) - public void testGetNfcBarcodeInstanceWithException() { - Tag mockTag = mock(Tag.class); - when(mockTag.hasTech(TagTechnology.NFC_BARCODE)).thenReturn(true); - when(mockTag.getTechExtras(TagTechnology.NFC_BARCODE)).thenReturn(null); - - assertNull(NfcBarcode.get(mockTag)); - verify(mockTag).hasTech(TagTechnology.NFC_BARCODE); - verify(mockTag).getTechExtras(TagTechnology.NFC_BARCODE); - } - - @Test - public void testGetNfcBarcodeWithoutTech() { - when(mMockTag.hasTech(TagTechnology.NFC_BARCODE)).thenReturn(false); - - assertNull(NfcBarcode.get(mMockTag)); - } - - @Test - public void testGetType() { - int result = mNfcBarcode.getType(); - assertEquals(TYPE_KOVIO, result); - } - - @Test - public void testGetBarcodeWithTypeKovio() { - byte[] sampleId = "sampleId".getBytes(); - when(mMockTag.getId()).thenReturn(sampleId); - - assertEquals(sampleId, mNfcBarcode.getBarcode()); - verify(mMockTag).getId(); - } - - @Test - public void testGetBarCodeTypeUnknown() throws RemoteException { - when(mMockBundle.getInt(EXTRA_BARCODE_TYPE)).thenReturn(TYPE_UNKNOWN); - when(mMockTag.getTechExtras(TagTechnology.NFC_BARCODE)).thenReturn(mMockBundle); - mNfcBarcode = new NfcBarcode(mMockTag); - - assertNull(mNfcBarcode.getBarcode()); - verify(mMockTag, never()).getId(); - } -} diff --git a/nfc/tests/src/android/nfc/tech/NfcFTest.java b/nfc/tests/src/android/nfc/tech/NfcFTest.java deleted file mode 100644 index 31a6943566e0..000000000000 --- a/nfc/tests/src/android/nfc/tech/NfcFTest.java +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright (C) 2010 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.tech; - -import static android.nfc.tech.NfcF.EXTRA_PMM; -import static android.nfc.tech.NfcF.EXTRA_SC; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThrows; -import static org.junit.Assert.fail; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.nfc.ErrorCodes; -import android.nfc.INfcTag; -import android.nfc.Tag; -import android.nfc.TransceiveResult; -import android.os.Bundle; -import android.os.RemoteException; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.io.IOException; - -public class NfcFTest { - private final byte[] mSampleSystemCode = new byte[] {1, 2, 3}; - private final byte[] mSampleManufacturer = new byte[] {3, 2, 1}; - @Mock - private Tag mMockTag; - @Mock - private INfcTag mMockTagService; - @Mock - private Bundle mMockBundle; - private NfcF mNfcF; - - @Before - public void setUp() throws RemoteException { - MockitoAnnotations.initMocks(this); - when(mMockBundle.getByteArray(EXTRA_SC)).thenReturn(mSampleSystemCode); - when(mMockBundle.getByteArray(EXTRA_PMM)).thenReturn(mSampleManufacturer); - when(mMockTag.getTechExtras(TagTechnology.NFC_F)).thenReturn(mMockBundle); - - mNfcF = new NfcF(mMockTag); - } - - @Test - public void testGetSystemCode() { - assertNotNull(mNfcF.getSystemCode()); - } - - @Test - public void testGetManufacturer() { - assertNotNull(mNfcF.getManufacturer()); - } - - @Test - public void testGetNfcFInstanceWithTech() { - Tag tag = mock(Tag.class); - when(tag.getTechExtras(TagTechnology.NFC_F)).thenReturn(mMockBundle); - when(tag.hasTech(TagTechnology.NFC_F)).thenReturn(true); - - assertNotNull(NfcF.get(tag)); - verify(tag).getTechExtras(TagTechnology.NFC_F); - verify(tag).hasTech(TagTechnology.NFC_F); - } - - @Test - public void testGetNfcFInstanceWithoutTech() { - Tag tag = mock(Tag.class); - when(tag.hasTech(TagTechnology.NFC_F)).thenReturn(false); - - assertNull(NfcF.get(tag)); - verify(tag).hasTech(TagTechnology.NFC_F); - verify(tag, never()).getTechExtras(TagTechnology.NFC_F); - } - - @Test - public void testTransceive() throws IOException, RemoteException { - byte[] sampleData = new byte[]{1, 2, 3, 4, 5}; - TransceiveResult mockTransceiveResult = mock(TransceiveResult.class); - when(mMockTag.getConnectedTechnology()).thenReturn(TagTechnology.NFC_F); - when(mMockTag.getTagService()).thenReturn(mMockTagService); - when(mMockTag.getServiceHandle()).thenReturn(1); - when(mMockTagService.transceive(1, sampleData, true)) - .thenReturn(mockTransceiveResult); - when(mockTransceiveResult.getResponseOrThrow()).thenReturn(sampleData); - - mNfcF.transceive(sampleData); - verify(mMockTag).getTagService(); - verify(mMockTag).getServiceHandle(); - } - - @Test - public void testGetMaxTransceiveLength() throws RemoteException { - when(mMockTag.getTagService()).thenReturn(mMockTagService); - when(mMockTagService.getMaxTransceiveLength(TagTechnology.NFC_F)).thenReturn(1); - - mNfcF.getMaxTransceiveLength(); - verify(mMockTag).getTagService(); - } - - @Test - public void testGetTimeout() { - when(mMockTag.getTagService()).thenReturn(mMockTagService); - try { - when(mMockTagService.getTimeout(TagTechnology.NFC_F)).thenReturn(2000); - - assertEquals(2000, mNfcF.getTimeout()); - verify(mMockTag).getTagService(); - verify(mMockTagService).getTimeout(TagTechnology.NFC_F); - } catch (Exception e) { - fail("Unexpected exception during valid getTimeout: " + e.getMessage()); - } - } - - @Test - public void testGetTimeoutRemoteException() { - when(mMockTag.getTagService()).thenReturn(mMockTagService); - try { - when(mMockTagService.getTimeout(TagTechnology.NFC_F)).thenThrow(new RemoteException()); - - assertEquals(0, mNfcF.getTimeout()); - } catch (Exception e) { - fail("Unexpected exception during RemoteException in getTimeout: " + e.getMessage()); - } - } - - @Test - public void testSetTimeout() { - when(mMockTag.getTagService()).thenReturn(mMockTagService); - try { - when(mMockTagService.setTimeout(TagTechnology.NFC_F, 1000)).thenReturn( - ErrorCodes.SUCCESS); - - mNfcF.setTimeout(1000); - verify(mMockTag).getTagService(); - verify(mMockTagService).setTimeout(TagTechnology.NFC_F, 1000); - } catch (Exception e) { - fail("Unexpected exception during valid setTimeout: " + e.getMessage()); - } - } - - @Test - public void testSetTimeoutInvalidTimeout() { - when(mMockTag.getTagService()).thenReturn(mMockTagService); - try { - when(mMockTagService.setTimeout(TagTechnology.NFC_F, -1)).thenReturn( - ErrorCodes.ERROR_TIMEOUT); - - assertThrows(IllegalArgumentException.class, () -> mNfcF.setTimeout(-1)); - } catch (Exception e) { - fail("Unexpected exception during invalid setTimeout: " + e.getMessage()); - } - } - - @Test - public void testSetTimeoutRemoteException() { - when(mMockTag.getTagService()).thenReturn(mMockTagService); - try { - when(mMockTagService.setTimeout(TagTechnology.NFC_F, 1000)).thenThrow( - new RemoteException()); - - mNfcF.setTimeout(1000); - verify(mMockTag).getTagService(); - verify(mMockTagService).setTimeout(TagTechnology.NFC_F, 1000); - } catch (Exception e) { - fail("Unexpected exception during RemoteException in setTimeout: " + e.getMessage()); - } - } -} diff --git a/nfc/tests/src/android/nfc/tech/NfcVTest.java b/nfc/tests/src/android/nfc/tech/NfcVTest.java deleted file mode 100644 index 6a99921b9287..000000000000 --- a/nfc/tests/src/android/nfc/tech/NfcVTest.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (C) 2010 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.tech; - -import static android.nfc.tech.NfcV.EXTRA_DSFID; -import static android.nfc.tech.NfcV.EXTRA_RESP_FLAGS; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.nfc.INfcTag; -import android.nfc.Tag; -import android.nfc.TransceiveResult; -import android.os.Bundle; -import android.os.RemoteException; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.io.IOException; - -public class NfcVTest { - private final byte mSampleRespFlags = (byte) 1; - private final byte mSampleDsfId = (byte) 2; - @Mock - private Tag mMockTag; - @Mock - private INfcTag mMockTagService; - @Mock - private Bundle mMockBundle; - private NfcV mNfcV; - - @Before - public void setUp() throws RemoteException { - MockitoAnnotations.initMocks(this); - when(mMockBundle.getByte(EXTRA_RESP_FLAGS)).thenReturn(mSampleRespFlags); - when(mMockBundle.getByte(EXTRA_DSFID)).thenReturn(mSampleDsfId); - when(mMockTag.getTechExtras(TagTechnology.NFC_V)).thenReturn(mMockBundle); - - mNfcV = new NfcV(mMockTag); - } - - @Test - public void testGetResponseFlag() { - assertEquals(mSampleRespFlags, mNfcV.getResponseFlags()); - } - - @Test - public void testGetDsfId() { - assertEquals(mSampleDsfId, mNfcV.getDsfId()); - } - - @Test - public void testGetNfcVInstance() { - Tag tag = mock(Tag.class); - when(tag.hasTech(TagTechnology.NFC_V)).thenReturn(true); - when(tag.getTechExtras(TagTechnology.NFC_V)).thenReturn(mMockBundle); - - assertNotNull(NfcV.get(tag)); - verify(tag).getTechExtras(TagTechnology.NFC_V); - verify(tag).hasTech(TagTechnology.NFC_V); - } - - @Test - public void testGetNfcVNullInstance() { - Tag tag = mock(Tag.class); - when(tag.hasTech(TagTechnology.NFC_V)).thenReturn(false); - - assertNull(NfcV.get(tag)); - verify(tag, never()).getTechExtras(TagTechnology.NFC_V); - verify(tag).hasTech(TagTechnology.NFC_V); - } - - @Test - public void testTransceive() throws IOException, RemoteException { - byte[] sampleData = new byte[] {1, 2, 3, 4, 5}; - TransceiveResult mockTransceiveResult = mock(TransceiveResult.class); - when(mMockTag.getConnectedTechnology()).thenReturn(TagTechnology.NFC_V); - when(mMockTag.getTagService()).thenReturn(mMockTagService); - when(mMockTag.getServiceHandle()).thenReturn(1); - when(mMockTagService.transceive(1, sampleData, true)) - .thenReturn(mockTransceiveResult); - when(mockTransceiveResult.getResponseOrThrow()).thenReturn(sampleData); - - mNfcV.transceive(sampleData); - verify(mMockTag).getTagService(); - verify(mMockTag).getServiceHandle(); - } - - @Test - public void testGetMaxTransceiveLength() throws RemoteException { - when(mMockTag.getTagService()).thenReturn(mMockTagService); - when(mMockTagService.getMaxTransceiveLength(TagTechnology.NFC_V)).thenReturn(1); - - mNfcV.getMaxTransceiveLength(); - verify(mMockTag).getTagService(); - } -} diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/ActionButton.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/ActionButton.kt index 04a2c07da388..b88fda77144f 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/ActionButton.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/ActionButton.kt @@ -65,8 +65,8 @@ fun ToggleVisibilityButton(modifier: Modifier = Modifier, onToggle: (Boolean) -> imageVector = if (toggleState.value) Icons.Outlined.Visibility else Icons.Outlined.VisibilityOff, contentDescription = if (toggleState.value) - stringResource(R.string.content_description_show_password) else - stringResource(R.string.content_description_hide_password), + stringResource(R.string.content_description_hide_password) else + stringResource(R.string.content_description_show_password), tint = MaterialTheme.colorScheme.onSurfaceVariant, ) } diff --git a/packages/SettingsLib/Preference/testutils/com/android/settingslib/preference/PreferenceBindingTestUtils.kt b/packages/SettingsLib/Preference/testutils/com/android/settingslib/preference/PreferenceBindingTestUtils.kt index 00bad5203f07..220614bf064f 100644 --- a/packages/SettingsLib/Preference/testutils/com/android/settingslib/preference/PreferenceBindingTestUtils.kt +++ b/packages/SettingsLib/Preference/testutils/com/android/settingslib/preference/PreferenceBindingTestUtils.kt @@ -19,18 +19,27 @@ package com.android.settingslib.preference import android.content.Context import androidx.annotation.VisibleForTesting import androidx.preference.Preference +import androidx.preference.PreferenceScreen import com.android.settingslib.metadata.PersistentPreference import com.android.settingslib.metadata.PreferenceMetadata /** Creates [Preference] widget and binds with metadata. */ +@Suppress("UNCHECKED_CAST") @VisibleForTesting -fun <P : Preference> PreferenceMetadata.createAndBindWidget(context: Context): P { +fun <P : Preference> PreferenceMetadata.createAndBindWidget( + context: Context, + preferenceScreen: PreferenceScreen? = null, +): P { val binding = PreferenceBindingFactory.defaultFactory.getPreferenceBinding(this)!! return (binding.createWidget(context) as P).also { if (this is PersistentPreference<*>) { - storage(context)?.let { keyValueStore -> + storage(context).let { keyValueStore -> it.preferenceDataStore = PreferenceDataStoreAdapter(keyValueStore) } + // Attach preference to preference screen, otherwise `Preference.performClick` does not + // interact with underlying datastore + (preferenceScreen ?: PreferenceScreenFactory(context).getOrCreatePreferenceScreen()) + .addPreference(it) } binding.bind(it, this) } diff --git a/packages/SettingsLib/SettingsTheme/res/color/settingslib_expressive_button_outline_color_selector.xml b/packages/SettingsLib/SettingsTheme/res/color/settingslib_expressive_button_outline_color_selector.xml new file mode 100644 index 000000000000..5084f11afa51 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/color/settingslib_expressive_button_outline_color_selector.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 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. + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_checked="true" android:color="@android:color/transparent"/> + <item android:color="?attr/colorOutlineVariant"/> +</selector>
\ No newline at end of file diff --git a/packages/SettingsLib/SettingsTheme/res/color/settingslib_expressive_button_outlined_background_color_selector.xml b/packages/SettingsLib/SettingsTheme/res/color/settingslib_expressive_button_outlined_background_color_selector.xml new file mode 100644 index 000000000000..ed05cdd1c96d --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/color/settingslib_expressive_button_outlined_background_color_selector.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 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. + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_enabled="false" android:state_checked="true" + android:alpha="0.12" + android:color="?attr/colorOnSurface"/> + <item android:state_checked="true" android:color="?attr/colorContainerChecked"/> + <item android:state_checkable="true" android:color="?attr/colorContainerUnchecked"/> + <item android:color="?attr/colorContainer" /> +</selector>
\ No newline at end of file diff --git a/packages/SettingsLib/SettingsTheme/res/color/settingslib_expressive_filled_button_color.xml b/packages/SettingsLib/SettingsTheme/res/color/settingslib_expressive_filled_button_color.xml new file mode 100644 index 000000000000..678bec4f3cba --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/color/settingslib_expressive_filled_button_color.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 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. + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_enabled="false" + android:alpha="0.12" android:color="@color/settingslib_materialColorOnSurface"/> + <item android:state_checked="true" android:color="@color/settingslib_materialColorPrimary"/> + <item android:state_checkable="true" android:color="@color/settingslib_materialColorSurfaceContainer"/> + <item android:color="@color/settingslib_materialColorPrimary" /> +</selector>
\ No newline at end of file diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_collapsable_textview.xml b/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_collapsable_textview.xml index 2261e58a961e..2776544e2948 100644 --- a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_collapsable_textview.xml +++ b/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_collapsable_textview.xml @@ -57,6 +57,7 @@ android:id="@+id/collapse_button" app:layout_constraintTop_toBottomOf="@id/settingslib_expressive_learn_more" app:layout_constraintStart_toStartOf="parent" + android:textColor="@color/settingslib_materialColorOnSurface" android:text="@string/settingslib_expressive_text_expand" app:icon="@drawable/settingslib_expressive_icon_expand" style="@style/SettingslibTextButtonStyle.Expressive"/> diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/styles_expressive.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/styles_expressive.xml index 9d3d70b366aa..b5f22b73cecd 100644 --- a/packages/SettingsLib/SettingsTheme/res/values-v31/styles_expressive.xml +++ b/packages/SettingsLib/SettingsTheme/res/values-v31/styles_expressive.xml @@ -17,27 +17,16 @@ <resources> <style name="SettingsLibButtonStyle.Expressive.Filled" - parent="@style/Widget.Material3.Button"> + parent="@style/Widget.Material3Expressive.Button.Icon"> <item name="android:theme">@style/Theme.Material3.DynamicColors.DayNight</item> <item name="android:layout_width">wrap_content</item> <item name="android:layout_height">wrap_content</item> <item name="android:gravity">center</item> - <item name="android:minWidth">@dimen/settingslib_expressive_space_medium4</item> - <item name="android:minHeight">@dimen/settingslib_expressive_space_medium4</item> - <item name="android:paddingVertical">@dimen/settingslib_expressive_space_extrasmall5</item> - <item name="android:paddingHorizontal">@dimen/settingslib_expressive_space_small1</item> - <item name="android:backgroundTint">@color/settingslib_materialColorPrimary</item> <item name="android:textAppearance">@style/TextAppearance.SettingsLib.LabelLarge</item> - <item name="android:textColor">@color/settingslib_materialColorOnPrimary</item> - <item name="iconGravity">textStart</item> - <item name="iconTint">@color/settingslib_materialColorOnPrimary</item> - <item name="iconSize">@dimen/settingslib_expressive_space_small4</item> </style> <style name="SettingsLibButtonStyle.Expressive.Filled.Large"> - <item name="android:paddingVertical">@dimen/settingslib_expressive_space_small1</item> - <item name="android:paddingHorizontal">@dimen/settingslib_expressive_space_small4</item> - <item name="android:textAppearance">@style/TextAppearance.SettingsLib.TitleMedium</item> + <item name="materialSizeOverlay">@style/SizeOverlay.Material3Expressive.Button.Medium</item> </style> <style name="SettingsLibButtonStyle.Expressive.Filled.Extra" @@ -45,28 +34,34 @@ <item name="android:layout_width">match_parent</item> </style> + <style name="SettingsLibButtonStyle.Expressive.Icon.Filled" + parent="@style/Widget.Material3Expressive.Button.IconButton.Filled"> + <item name="android:theme">@style/Theme.Material3.DynamicColors.DayNight</item> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:gravity">center</item> + </style> + + <style name="SettingsLibButtonStyle.Expressive.Icon.Filled.Large"> + <item name="materialSizeOverlay">@style/SizeOverlay.Material3Expressive.Button.Medium</item> + </style> + + <style name="SettingsLibButtonStyle.Expressive.Icon.Filled.Extra" + parent="@style/SettingsLibButtonStyle.Expressive.Icon.Filled.Large"> + <item name="android:layout_width">match_parent</item> + </style> + <style name="SettingsLibButtonStyle.Expressive.Tonal" - parent="@style/Widget.Material3.Button.TonalButton"> + parent="@style/Widget.Material3Expressive.Button.TonalButton.Icon"> <item name="android:theme">@style/Theme.Material3.DynamicColors.DayNight</item> <item name="android:layout_width">wrap_content</item> <item name="android:layout_height">wrap_content</item> <item name="android:gravity">center</item> - <item name="android:minWidth">@dimen/settingslib_expressive_space_medium4</item> - <item name="android:minHeight">@dimen/settingslib_expressive_space_medium4</item> - <item name="android:paddingVertical">@dimen/settingslib_expressive_space_extrasmall5</item> - <item name="android:paddingHorizontal">@dimen/settingslib_expressive_space_small1</item> - <item name="android:backgroundTint">@color/settingslib_materialColorSecondaryContainer</item> <item name="android:textAppearance">@style/TextAppearance.SettingsLib.LabelLarge</item> - <item name="android:textColor">@color/settingslib_materialColorOnSecondaryContainer</item> - <item name="iconGravity">textStart</item> - <item name="iconTint">@color/settingslib_materialColorOnSecondaryContainer</item> - <item name="iconSize">@dimen/settingslib_expressive_space_small4</item> </style> <style name="SettingsLibButtonStyle.Expressive.Tonal.Large"> - <item name="android:paddingVertical">@dimen/settingslib_expressive_space_small1</item> - <item name="android:paddingHorizontal">@dimen/settingslib_expressive_space_small4</item> - <item name="android:textAppearance">@style/TextAppearance.SettingsLib.TitleMedium</item> + <item name="materialSizeOverlay">@style/SizeOverlay.Material3Expressive.Button.Medium</item> </style> <style name="SettingsLibButtonStyle.Expressive.Tonal.Extra" @@ -74,29 +69,34 @@ <item name="android:layout_width">match_parent</item> </style> + <style name="SettingsLibButtonStyle.Expressive.Icon.Tonal" + parent="@style/Widget.Material3Expressive.Button.IconButton.Tonal"> + <item name="android:theme">@style/Theme.Material3.DynamicColors.DayNight</item> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:gravity">center</item> + </style> + + <style name="SettingsLibButtonStyle.Expressive.Icon.Tonal.Large"> + <item name="materialSizeOverlay">@style/SizeOverlay.Material3Expressive.Button.Medium</item> + </style> + + <style name="SettingsLibButtonStyle.Expressive.Icon.Tonal.Extra" + parent="@style/SettingsLibButtonStyle.Expressive.Tonal.Large"> + <item name="android:layout_width">match_parent</item> + </style> + <style name="SettingsLibButtonStyle.Expressive.Outline" - parent="@style/Widget.Material3.Button.OutlinedButton.Icon"> + parent="@style/Widget.Material3Expressive.Button.OutlinedButton.Icon"> <item name="android:theme">@style/Theme.Material3.DynamicColors.DayNight</item> <item name="android:layout_width">wrap_content</item> <item name="android:layout_height">wrap_content</item> <item name="android:gravity">center</item> - <item name="android:minWidth">@dimen/settingslib_expressive_space_medium4</item> - <item name="android:minHeight">@dimen/settingslib_expressive_space_medium4</item> - <item name="android:paddingVertical">@dimen/settingslib_expressive_space_extrasmall5</item> - <item name="android:paddingHorizontal">@dimen/settingslib_expressive_space_small1</item> <item name="android:textAppearance">@style/TextAppearance.SettingsLib.LabelLarge</item> - <item name="android:textColor">@color/settingslib_materialColorPrimary</item> - <item name="iconTint">@color/settingslib_materialColorPrimary</item> - <item name="iconGravity">textStart</item> - <item name="iconSize">@dimen/settingslib_expressive_space_small4</item> - <item name="iconPadding">@dimen/settingslib_expressive_space_extrasmall4</item> - <item name="strokeColor">@color/settingslib_materialColorOutlineVariant</item> </style> <style name="SettingsLibButtonStyle.Expressive.Outline.Large"> - <item name="android:paddingVertical">@dimen/settingslib_expressive_space_small1</item> - <item name="android:paddingHorizontal">@dimen/settingslib_expressive_space_small4</item> - <item name="android:textAppearance">@style/TextAppearance.SettingsLib.TitleMedium</item> + <item name="materialSizeOverlay">@style/SizeOverlay.Material3Expressive.Button.Medium</item> </style> <style name="SettingsLibButtonStyle.Expressive.Outline.Extra" @@ -105,15 +105,10 @@ </style> <style name="SettingslibTextButtonStyle.Expressive" - parent="@style/Widget.Material3.Button.TextButton.Icon"> + parent="@style/Widget.Material3Expressive.Button.TextButton.Icon"> <item name="android:theme">@style/Theme.Material3.DynamicColors.DayNight</item> <item name="android:layout_width">wrap_content</item> <item name="android:layout_height">wrap_content</item> - <item name="android:textAppearance">@style/TextAppearance.SettingsLib.BodyLarge.Emphasized</item> - <item name="android:textColor">@color/settingslib_materialColorOnSurface</item> - <item name="iconTint">@null</item> - <item name="iconPadding">@dimen/settingslib_expressive_space_extrasmall4</item> - <item name="rippleColor">?android:attr/colorControlHighlight</item> </style> <style name="SettingsLibCardStyle" parent=""> diff --git a/packages/SettingsLib/SettingsTheme/res/values/styles_m3_expressive.xml b/packages/SettingsLib/SettingsTheme/res/values/styles_m3_expressive.xml new file mode 100644 index 000000000000..826fe31d121d --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values/styles_m3_expressive.xml @@ -0,0 +1,475 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 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. + --> + +<resources xmlns:tools="http://schemas.android.com/tools"> + <!-- M3 Expressive filled button style. --> + <style name="Widget.Material3Expressive.Button" parent="Widget.Material3.Button"> + <item name="android:paddingStart">?attr/containerPaddingStart</item> + <item name="android:paddingEnd">?attr/containerPaddingEnd</item> + <item name="android:paddingTop">?attr/containerPaddingTop</item> + <item name="android:paddingBottom">?attr/containerPaddingBottom</item> + <item name="android:insetTop">?attr/containerInsetTop</item> + <item name="android:insetBottom">?attr/containerInsetBottom</item> + <item name="iconPadding">?attr/containerIconPadding</item> + <item name="iconSize">?attr/containerIconSize</item> + <item name="android:textAppearance">?attr/labelTextAppearance</item> + <item name="shapeAppearance">@xml/settingslib_button_shape_state_list</item> + <item name="shapeAppearanceOverlay">@null</item> + <item name="materialThemeOverlay">@style/ThemeOverlay.Material3Expressive.Button.Filled</item> + <item name="materialSizeOverlay">@style/SizeOverlay.Material3Expressive.Button.Small</item> + </style> + <style name="Widget.Material3Expressive.Button.Icon"/> + + <!-- M3 Expressive tonal button style. --> + <style name="Widget.Material3Expressive.Button.TonalButton"> + <item name="materialThemeOverlay">@style/ThemeOverlay.Material3Expressive.Button.Tonal</item> + </style> + <style name="Widget.Material3Expressive.Button.TonalButton.Icon"/> + + <!-- M3 Expressive outlined button style. --> + <style name="Widget.Material3Expressive.Button.OutlinedButton"> + <item name="android:stateListAnimator" tools:ignore="NewApi">@animator/mtrl_btn_unelevated_state_list_anim</item> + <item name="elevation">0dp</item> + <item name="materialThemeOverlay">@style/ThemeOverlay.Material3Expressive.Button.Outlined</item> + <item name="strokeColor">@color/settingslib_expressive_button_outline_color_selector</item> + <item name="strokeWidth">?attr/containerStrokeWidth</item> + <item name="backgroundTint">@color/settingslib_expressive_button_outlined_background_color_selector</item> + </style> + <style name="Widget.Material3Expressive.Button.OutlinedButton.Icon"/> + + <!-- M3 Expressive text button style. --> + <style name="Widget.Material3Expressive.Button.TextButton"> + <item name="android:stateListAnimator" tools:ignore="NewApi">@animator/mtrl_btn_unelevated_state_list_anim</item> + <item name="elevation">0dp</item> + <item name="materialThemeOverlay">@style/ThemeOverlay.Material3Expressive.Button.TextButton</item> + </style> + <style name="Widget.Material3Expressive.Button.TextButton.Icon"/> + + <!-- Styles for M3 Expressive Icon Buttons. --> + + <!-- M3 Expressive icon only button without a container or outline style. --> + <style name="Widget.Material3Expressive.Button.IconButton"> + <item name="android:stateListAnimator" tools:ignore="NewApi">@animator/mtrl_btn_unelevated_state_list_anim</item> + <item name="elevation">0dp</item> + <item name="android:minWidth">@dimen/settingslib_expressive_space_medium4</item> + <item name="android:minHeight">@dimen/settingslib_expressive_space_medium4</item> + <item name="android:insetLeft">?attr/containerInsetLeft</item> + <item name="android:insetRight">?attr/containerInsetRight</item> + <item name="iconPadding">@dimen/m3_btn_icon_only_icon_padding</item> + <item name="materialThemeOverlay">@style/ThemeOverlay.Material3Expressive.Button.IconButton.Standard</item> + <item name="materialSizeOverlay">@style/SizeOverlay.Material3Expressive.Button.IconButton.Small</item> + </style> + + <!-- M3 Expressive icon only button filled container style. --> + <style name="Widget.Material3Expressive.Button.IconButton.Filled"> + <item name="materialThemeOverlay">@style/ThemeOverlay.Material3Expressive.Button.Filled</item> + </style> + + <!-- M3 Expressive icon only button in tonal container style. --> + <style name="Widget.Material3Expressive.Button.IconButton.Tonal"> + <item name="materialThemeOverlay">@style/ThemeOverlay.Material3Expressive.Button.Tonal</item> + </style> + + <!-- M3 Expressive icon only button with an outline style. --> + <style name="Widget.Material3Expressive.Button.IconButton.Outlined"> + <item name="materialThemeOverlay">@style/ThemeOverlay.Material3Expressive.Button.Outlined</item> + <item name="strokeColor">@color/settingslib_expressive_button_outline_color_selector</item> + <item name="strokeWidth">?attr/containerStrokeWidth</item> + <item name="backgroundTint">@color/settingslib_expressive_button_outlined_background_color_selector</item> + </style> + + <!-- Styles for M3 Expressive Button Groups. --> + + <!-- M3 Expressive Button Group. --> + <style name="Widget.Material3Expressive.MaterialButtonGroup" parent="Widget.Material3.MaterialButtonGroup"/> + + <!-- M3 Expressive Connected Button Group. --> + <style name="Widget.Material3Expressive.MaterialButtonGroup.Connected" parent="Widget.Material3.MaterialButtonGroup.Connected"> + <item name="innerCornerSize">@xml/settingslib_inner_corner_size_state_list</item> + </style> + + <!-- M3 Expressive Button Toggle Group (Segmented Button). --> + <style name="Widget.Material3Expressive.MaterialButtonToggleGroup" parent="Widget.Material3.MaterialButtonToggleGroup"> + <item name="innerCornerSize">@xml/settingslib_inner_corner_size_state_list</item> + <item name="shapeAppearance">@style/ShapeAppearance.Material3.Corner.Full</item> + <item name="android:spacing">@dimen/settingslib_expressive_space_extrasmall1</item> + </style> + + <!-- M3 Expressive Button Theme Overlays for different color variants. --> + + <!-- M3 Expressive Button Theme Overlay for the filled color variant. --> + <style name="ThemeOverlay.Material3Expressive.Button.Filled" parent=""> + <item name="colorContainer">?attr/colorPrimary</item> + <item name="colorContainerChecked">?attr/colorPrimary</item> + <item name="colorContainerUnchecked">?attr/colorSurfaceContainer</item> + <item name="colorOnContainer">?attr/colorOnPrimary</item> + <item name="colorOnContainerChecked">?attr/colorOnPrimary</item> + <item name="colorOnContainerUnchecked">?attr/colorOnSurfaceVariant</item> + </style> + + <!-- M3 Expressive Button Theme Overlay for the tonal color variant. --> + <style name="ThemeOverlay.Material3Expressive.Button.Tonal" parent=""> + <item name="colorContainer">?attr/colorSecondaryContainer</item> + <item name="colorContainerChecked">?attr/colorSecondary</item> + <item name="colorContainerUnchecked">?attr/colorSecondaryContainer</item> + <item name="colorOnContainer">?attr/colorOnSecondaryContainer</item> + <item name="colorOnContainerChecked">?attr/colorOnSecondary</item> + <item name="colorOnContainerUnchecked">?attr/colorOnSecondaryContainer</item> + </style> + + <!-- M3 Expressive Button Theme Overlay for the outlined variant. --> + <style name="ThemeOverlay.Material3Expressive.Button.Outlined" parent=""> + <item name="colorContainer">@android:color/transparent</item> + <item name="colorContainerChecked">?attr/colorSurfaceInverse</item> + <item name="colorContainerUnchecked">@android:color/transparent</item> + <item name="colorOnContainer">?attr/colorOnSurfaceVariant</item> + <item name="colorOnContainerChecked">?attr/colorOnSurfaceInverse</item> + <item name="colorOnContainerUnchecked">?attr/colorOnSurfaceVariant</item> + </style> + + <!-- M3 Expressive Button Theme Overlay for the text only variant. --> + <style name="ThemeOverlay.Material3Expressive.Button.TextButton" parent="ThemeOverlay.Material3.Button.TextButton"> + <item name="colorContainerChecked">?attr/colorContainer</item> + <item name="colorContainerUnchecked">?attr/colorContainer</item> + <item name="colorOnContainerChecked">?attr/colorOnContainer</item> + <item name="colorOnContainerUnchecked">?attr/colorOnSurfaceVariant</item> + </style> + + <!-- M3 Expressive Button Theme Overlay for the icon only variant. --> + <style name="ThemeOverlay.Material3Expressive.Button.IconButton.Standard" parent=""> + <item name="colorContainer">@android:color/transparent</item> + <item name="colorContainerChecked">@android:color/transparent</item> + <item name="colorContainerUnchecked">@android:color/transparent</item> + <item name="colorOnContainer">?attr/colorOnSurfaceVariant</item> + <item name="colorOnContainerChecked">?attr/colorPrimary</item> + <item name="colorOnContainerUnchecked">?attr/colorOnSurfaceVariant</item> + </style> + + <!-- M3 Expressive Button Theme Overlay for the extra small variant. --> + <style name="SizeOverlay.Material3Expressive.Button.Xsmall" parent=""> + <item name="containerPaddingStart">@dimen/settingslib_expressive_space_extrasmall6</item> + <item name="containerPaddingEnd">@dimen/settingslib_expressive_space_extrasmall6</item> + <item name="containerPaddingTop">6dp</item> + <item name="containerPaddingBottom">6dp</item> + <item name="containerInsetTop">8dp</item> + <item name="containerInsetBottom">8dp</item> + <item name="containerIconSize">@dimen/settingslib_expressive_space_small3</item> + <item name="containerIconPadding">@dimen/settingslib_expressive_space_extrasmall4</item> + <item name="containerStrokeWidth">1dp</item> + <item name="labelTextAppearance">?attr/textAppearanceLabelLarge</item> + <item name="containerShapePressed">?attr/shapeAppearanceCornerSmall</item> + <item name="containerShapeChecked">?attr/shapeAppearanceCornerMedium</item> + <item name="containerShapeDefault">@style/ShapeAppearance.Material3.Corner.Full</item> + </style> + <style name="SizeOverlay.Material3Expressive.Button.Xsmall.Square"> + <item name="containerShapeChecked">@style/ShapeAppearance.Material3.Corner.Full</item> + <item name="containerShapeDefault">?attr/shapeAppearanceCornerMedium</item> + </style> + + <!-- M3 Expressive Button Theme Overlay for the small variant. --> + <style name="SizeOverlay.Material3Expressive.Button.Small" parent=""> + <item name="containerPaddingStart">@dimen/settingslib_expressive_space_small1</item> + <item name="containerPaddingEnd">@dimen/settingslib_expressive_space_small1</item> + <item name="containerPaddingTop">10dp</item> + <item name="containerPaddingBottom">10dp</item> + <item name="containerInsetTop">4dp</item> + <item name="containerInsetBottom">4dp</item> + <item name="containerIconSize">@dimen/settingslib_expressive_space_small3</item> + <item name="containerIconPadding">@dimen/settingslib_expressive_space_extrasmall4</item> + <item name="containerStrokeWidth">1dp</item> + <item name="labelTextAppearance">?attr/textAppearanceLabelLarge</item> + <item name="containerShapePressed">?attr/shapeAppearanceCornerSmall</item> + <item name="containerShapeChecked">?attr/shapeAppearanceCornerMedium</item> + <item name="containerShapeDefault">@style/ShapeAppearance.Material3.Corner.Full</item> + </style> + <style name="SizeOverlay.Material3Expressive.Button.Small.Square"> + <item name="containerShapeChecked">@style/ShapeAppearance.Material3.Corner.Full</item> + <item name="containerShapeDefault">?attr/shapeAppearanceCornerMedium</item> + </style> + + <!-- M3 Expressive Button Theme Overlay for the medium variant. --> + <style name="SizeOverlay.Material3Expressive.Button.Medium" parent=""> + <item name="containerPaddingStart">@dimen/settingslib_expressive_space_small4</item> + <item name="containerPaddingEnd">@dimen/settingslib_expressive_space_small4</item> + <item name="containerPaddingTop">16dp</item> + <item name="containerPaddingBottom">16dp</item> + <item name="containerInsetTop">0dp</item> + <item name="containerInsetBottom">0dp</item> + <item name="containerIconSize">@dimen/settingslib_expressive_space_small4</item> + <item name="containerIconPadding">@dimen/settingslib_expressive_space_extrasmall4</item> + <item name="containerStrokeWidth">1dp</item> + <item name="labelTextAppearance">?attr/textAppearanceTitleMedium</item> + <item name="containerShapePressed">?attr/shapeAppearanceCornerMedium</item> + <item name="containerShapeChecked">?attr/shapeAppearanceCornerLarge</item> + <item name="containerShapeDefault">@style/ShapeAppearance.Material3.Corner.Full</item> + </style> + <style name="SizeOverlay.Material3Expressive.Button.Medium.Square"> + <item name="containerShapeChecked">@style/ShapeAppearance.Material3.Corner.Full</item> + <item name="containerShapeDefault">?attr/shapeAppearanceCornerLarge</item> + </style> + + <!-- M3 Expressive Button Theme Overlay for the large variant. --> + <style name="SizeOverlay.Material3Expressive.Button.Large" parent=""> + <item name="containerPaddingStart">@dimen/settingslib_expressive_space_medium4</item> + <item name="containerPaddingEnd">@dimen/settingslib_expressive_space_medium4</item> + <item name="containerPaddingTop">32dp</item> + <item name="containerPaddingBottom">32dp</item> + <item name="containerInsetTop">0dp</item> + <item name="containerInsetBottom">0dp</item> + <item name="containerIconSize">@dimen/settingslib_expressive_space_medium1</item> + <item name="containerIconPadding">@dimen/settingslib_expressive_space_extrasmall6</item> + <item name="containerStrokeWidth">@dimen/settingslib_expressive_space_extrasmall1</item> + <item name="labelTextAppearance">?attr/textAppearanceHeadlineSmall</item> + <item name="containerShapePressed">?attr/shapeAppearanceCornerLarge</item> + <item name="containerShapeChecked">?attr/shapeAppearanceCornerExtraLarge</item> + <item name="containerShapeDefault">@style/ShapeAppearance.Material3.Corner.Full</item> + </style> + <style name="SizeOverlay.Material3Expressive.Button.Large.Square"> + <item name="containerShapeChecked">@style/ShapeAppearance.Material3.Corner.Full</item> + <item name="containerShapeDefault">?attr/shapeAppearanceCornerExtraLarge</item> + </style> + + <!-- M3 Expressive Button Theme Overlay for the extra large variant. --> + <style name="SizeOverlay.Material3Expressive.Button.Xlarge" parent=""> + <item name="containerPaddingStart">@dimen/settingslib_expressive_space_large2</item> + <item name="containerPaddingEnd">@dimen/settingslib_expressive_space_large2</item> + <item name="containerPaddingTop">48dp</item> + <item name="containerPaddingBottom">48dp</item> + <item name="containerInsetTop">0dp</item> + <item name="containerInsetBottom">0dp</item> + <item name="containerIconSize">@dimen/settingslib_expressive_space_medium3</item> + <item name="containerIconPadding">@dimen/settingslib_expressive_space_small1</item> + <item name="containerStrokeWidth">3dp</item> + <item name="labelTextAppearance">?attr/textAppearanceHeadlineLarge</item> + <item name="containerShapePressed">?attr/shapeAppearanceCornerLarge</item> + <item name="containerShapeChecked">?attr/shapeAppearanceCornerExtraLarge</item> + <item name="containerShapeDefault">@style/ShapeAppearance.Material3.Corner.Full</item> + </style> + <style name="SizeOverlay.Material3Expressive.Button.Xlarge.Square"> + <item name="containerShapeChecked">@style/ShapeAppearance.Material3.Corner.Full</item> + <item name="containerShapeDefault">?attr/shapeAppearanceCornerExtraLarge</item> + </style> + + <!-- M3 Expressive Icon Button Theme Overlay for the extra small variant. --> + <style name="SizeOverlay.Material3Expressive.Button.IconButton.Xsmall" parent=""> + <item name="containerPaddingStart">@dimen/settingslib_expressive_space_extrasmall3</item> + <item name="containerPaddingEnd">@dimen/settingslib_expressive_space_extrasmall3</item> + <item name="containerPaddingTop">6dp</item> + <item name="containerPaddingBottom">6dp</item> + <item name="containerInsetTop">8dp</item> + <item name="containerInsetBottom">8dp</item> + <item name="containerInsetLeft">8dp</item> + <item name="containerInsetRight">8dp</item> + <item name="containerIconSize">@dimen/settingslib_expressive_space_small3</item> + <item name="containerStrokeWidth">1dp</item> + <item name="containerShapePressed">?attr/shapeAppearanceCornerSmall</item> + <item name="containerShapeChecked">?attr/shapeAppearanceCornerMedium</item> + <item name="containerShapeDefault">@style/ShapeAppearance.Material3.Corner.Full</item> + </style> + <style name="SizeOverlay.Material3Expressive.Button.IconButton.Xsmall.Square"> + <item name="containerShapeChecked">@style/ShapeAppearance.Material3.Corner.Full</item> + <item name="containerShapeDefault">?attr/shapeAppearanceCornerMedium</item> + </style> + + <style name="SizeOverlay.Material3Expressive.Button.IconButton.Xsmall.Narrow"> + <item name="containerPaddingStart">@dimen/settingslib_expressive_space_extrasmall2</item> + <item name="containerPaddingEnd">@dimen/settingslib_expressive_space_extrasmall2</item> + <item name="containerInsetLeft">10dp</item> + <item name="containerInsetRight">10dp</item> + </style> + <style name="SizeOverlay.Material3Expressive.Button.IconButton.Xsmall.Narrow.Square"> + <item name="containerShapeChecked">@style/ShapeAppearance.Material3.Corner.Full</item> + <item name="containerShapeDefault">?attr/shapeAppearanceCornerMedium</item> + </style> + + <style name="SizeOverlay.Material3Expressive.Button.IconButton.Xsmall.Wide"> + <item name="containerPaddingStart">@dimen/settingslib_expressive_space_extrasmall5</item> + <item name="containerPaddingEnd">@dimen/settingslib_expressive_space_extrasmall5</item> + <item name="containerInsetLeft">4dp</item> + <item name="containerInsetRight">4dp</item> + </style> + <style name="SizeOverlay.Material3Expressive.Button.IconButton.Xsmall.Wide.Square"> + <item name="containerShapeChecked">@style/ShapeAppearance.Material3.Corner.Full</item> + <item name="containerShapeDefault">?attr/shapeAppearanceCornerMedium</item> + </style> + + <!-- M3 Expressive Icon Button Theme Overlay for the small variant. --> + <style name="SizeOverlay.Material3Expressive.Button.IconButton.Small" parent=""> + <item name="containerPaddingStart">@dimen/settingslib_expressive_space_extrasmall4</item> + <item name="containerPaddingEnd">@dimen/settingslib_expressive_space_extrasmall4</item> + <item name="containerPaddingTop">8dp</item> + <item name="containerPaddingBottom">8dp</item> + <item name="containerInsetTop">4dp</item> + <item name="containerInsetBottom">4dp</item> + <item name="containerInsetLeft">4dp</item> + <item name="containerInsetRight">4dp</item> + <item name="containerIconSize">@dimen/settingslib_expressive_space_small4</item> + <item name="containerStrokeWidth">1dp</item> + <item name="containerShapePressed">?attr/shapeAppearanceCornerSmall</item> + <item name="containerShapeChecked">?attr/shapeAppearanceCornerMedium</item> + <item name="containerShapeDefault">@style/ShapeAppearance.Material3.Corner.Full</item> + </style> + <style name="SizeOverlay.Material3Expressive.Button.IconButton.Small.Square"> + <item name="containerShapeChecked">@style/ShapeAppearance.Material3.Corner.Full</item> + <item name="containerShapeDefault">?attr/shapeAppearanceCornerMedium</item> + </style> + + <style name="SizeOverlay.Material3Expressive.Button.IconButton.Small.Narrow"> + <item name="containerPaddingStart">@dimen/settingslib_expressive_space_extrasmall2</item> + <item name="containerPaddingEnd">@dimen/settingslib_expressive_space_extrasmall2</item> + <item name="containerInsetLeft">8dp</item> + <item name="containerInsetRight">8dp</item> + </style> + <style name="SizeOverlay.Material3Expressive.Button.IconButton.Small.Narrow.Square"> + <item name="containerShapeChecked">@style/ShapeAppearance.Material3.Corner.Full</item> + <item name="containerShapeDefault">?attr/shapeAppearanceCornerMedium</item> + </style> + + <style name="SizeOverlay.Material3Expressive.Button.IconButton.Small.Wide"> + <item name="containerPaddingStart">@dimen/settingslib_expressive_space_extrasmall7</item> + <item name="containerPaddingEnd">@dimen/settingslib_expressive_space_extrasmall7</item> + <item name="containerInsetLeft">0dp</item> + <item name="containerInsetRight">0dp</item> + </style> + <style name="SizeOverlay.Material3Expressive.Button.IconButton.Small.Wide.Square"> + <item name="containerShapeChecked">@style/ShapeAppearance.Material3.Corner.Full</item> + <item name="containerShapeDefault">?attr/shapeAppearanceCornerMedium</item> + </style> + + <!-- M3 Expressive Icon Button Theme Overlay for the medium variant. --> + <style name="SizeOverlay.Material3Expressive.Button.IconButton.Medium" parent=""> + <item name="containerPaddingStart">@dimen/settingslib_expressive_space_small1</item> + <item name="containerPaddingEnd">@dimen/settingslib_expressive_space_small1</item> + <item name="containerPaddingTop">16dp</item> + <item name="containerPaddingBottom">16dp</item> + <item name="containerInsetTop">0dp</item> + <item name="containerInsetBottom">0dp</item> + <item name="containerInsetLeft">0dp</item> + <item name="containerInsetRight">0dp</item> + <item name="containerIconSize">@dimen/settingslib_expressive_space_small4</item> + <item name="containerStrokeWidth">1dp</item> + <item name="containerShapePressed">?attr/shapeAppearanceCornerMedium</item> + <item name="containerShapeChecked">?attr/shapeAppearanceCornerLarge</item> + <item name="containerShapeDefault">@style/ShapeAppearance.Material3.Corner.Full</item> + </style> + <style name="SizeOverlay.Material3Expressive.Button.IconButton.Medium.Square"> + <item name="containerShapeChecked">@style/ShapeAppearance.Material3.Corner.Full</item> + <item name="containerShapeDefault">?attr/shapeAppearanceCornerLarge</item> + </style> + + <style name="SizeOverlay.Material3Expressive.Button.IconButton.Medium.Narrow"> + <item name="containerPaddingStart">@dimen/settingslib_expressive_space_extrasmall6</item> + <item name="containerPaddingEnd">@dimen/settingslib_expressive_space_extrasmall6</item> + </style> + <style name="SizeOverlay.Material3Expressive.Button.IconButton.Medium.Narrow.Square"> + <item name="containerShapeChecked">@style/ShapeAppearance.Material3.Corner.Full</item> + <item name="containerShapeDefault">?attr/shapeAppearanceCornerLarge</item> + </style> + + <style name="SizeOverlay.Material3Expressive.Button.IconButton.Medium.Wide"> + <item name="containerPaddingStart">@dimen/settingslib_expressive_space_small4</item> + <item name="containerPaddingEnd">@dimen/settingslib_expressive_space_small4</item> + </style> + <style name="SizeOverlay.Material3Expressive.Button.IconButton.Medium.Wide.Square"> + <item name="containerShapeChecked">@style/ShapeAppearance.Material3.Corner.Full</item> + <item name="containerShapeDefault">?attr/shapeAppearanceCornerLarge</item> + </style> + + <!-- M3 Expressive Icon Button Theme Overlay for the large variant. --> + <style name="SizeOverlay.Material3Expressive.Button.IconButton.Large" parent=""> + <item name="containerPaddingStart">@dimen/settingslib_expressive_space_medium1</item> + <item name="containerPaddingEnd">@dimen/settingslib_expressive_space_medium1</item> + <item name="containerPaddingTop">32dp</item> + <item name="containerPaddingBottom">32dp</item> + <item name="containerInsetTop">0dp</item> + <item name="containerInsetBottom">0dp</item> + <item name="containerInsetLeft">0dp</item> + <item name="containerInsetRight">0dp</item> + <item name="containerIconSize">@dimen/settingslib_expressive_space_medium1</item> + <item name="containerStrokeWidth">@dimen/settingslib_expressive_space_extrasmall1</item> + <item name="containerShapePressed">?attr/shapeAppearanceCornerLarge</item> + <item name="containerShapeChecked">?attr/shapeAppearanceCornerExtraLarge</item> + <item name="containerShapeDefault">@style/ShapeAppearance.Material3.Corner.Full</item> + </style> + <style name="SizeOverlay.Material3Expressive.Button.IconButton.Large.Square"> + <item name="containerShapeChecked">@style/ShapeAppearance.Material3.Corner.Full</item> + <item name="containerShapeDefault">?attr/shapeAppearanceCornerExtraLarge</item> + </style> + + <style name="SizeOverlay.Material3Expressive.Button.IconButton.Large.Narrow"> + <item name="containerPaddingStart">@dimen/settingslib_expressive_space_small1</item> + <item name="containerPaddingEnd">@dimen/settingslib_expressive_space_small1</item> + </style> + <style name="SizeOverlay.Material3Expressive.Button.IconButton.Large.Narrow.Square"> + <item name="containerShapeChecked">@style/ShapeAppearance.Material3.Corner.Full</item> + <item name="containerShapeDefault">?attr/shapeAppearanceCornerExtraLarge</item> + </style> + + <style name="SizeOverlay.Material3Expressive.Button.IconButton.Large.Wide"> + <item name="containerPaddingStart">@dimen/settingslib_expressive_space_medium4</item> + <item name="containerPaddingEnd">@dimen/settingslib_expressive_space_medium4</item> + </style> + <style name="SizeOverlay.Material3Expressive.Button.IconButton.Large.Wide.Square"> + <item name="containerShapeChecked">@style/ShapeAppearance.Material3.Corner.Full</item> + <item name="containerShapeDefault">?attr/shapeAppearanceCornerExtraLarge</item> + </style> + + <!-- M3 Expressive Icon Button Theme Overlay for the extra large variant. --> + <style name="SizeOverlay.Material3Expressive.Button.IconButton.Xlarge" parent=""> + <item name="containerPaddingStart">@dimen/settingslib_expressive_space_medium4</item> + <item name="containerPaddingEnd">@dimen/settingslib_expressive_space_medium4</item> + <item name="containerPaddingTop">48dp</item> + <item name="containerPaddingBottom">48dp</item> + <item name="containerInsetTop">0dp</item> + <item name="containerInsetBottom">0dp</item> + <item name="containerInsetLeft">0dp</item> + <item name="containerInsetRight">0dp</item> + <item name="containerIconSize">@dimen/settingslib_expressive_space_medium3</item> + <item name="containerStrokeWidth">3dp</item> + <item name="containerShapePressed">?attr/shapeAppearanceCornerLarge</item> + <item name="containerShapeChecked">?attr/shapeAppearanceCornerExtraLarge</item> + <item name="containerShapeDefault">@style/ShapeAppearance.Material3.Corner.Full</item> + </style> + <style name="SizeOverlay.Material3Expressive.Button.IconButton.Xlarge.Square"> + <item name="containerShapeChecked">@style/ShapeAppearance.Material3.Corner.Full</item> + <item name="containerShapeDefault">?attr/shapeAppearanceCornerExtraLarge</item> + </style> + + <style name="SizeOverlay.Material3Expressive.Button.IconButton.Xlarge.Narrow"> + <item name="containerPaddingStart">@dimen/settingslib_expressive_space_medium1</item> + <item name="containerPaddingEnd">@dimen/settingslib_expressive_space_medium1</item> + </style> + <style name="SizeOverlay.Material3Expressive.Button.IconButton.Xlarge.Narrow.Square"> + <item name="containerShapeChecked">@style/ShapeAppearance.Material3.Corner.Full</item> + <item name="containerShapeDefault">?attr/shapeAppearanceCornerExtraLarge</item> + </style> + + <style name="SizeOverlay.Material3Expressive.Button.IconButton.Xlarge.Wide"> + <item name="containerPaddingStart">@dimen/settingslib_expressive_space_large3</item> + <item name="containerPaddingEnd">@dimen/settingslib_expressive_space_large3</item> + </style> + <style name="SizeOverlay.Material3Expressive.Button.IconButton.Xlarge.Wide.Square"> + <item name="containerShapeChecked">@style/ShapeAppearance.Material3.Corner.Full</item> + <item name="containerShapeDefault">?attr/shapeAppearanceCornerExtraLarge</item> + </style> + + <!-- M3 shape --> + <style name="ShapeAppearance.Material3.Corner.Full" parent=""> + <item name="cornerFamily">?attr/shapeCornerFamily</item> + <item name="cornerSize">50%</item> + </style> +</resources>
\ No newline at end of file diff --git a/packages/SettingsLib/SettingsTheme/res/xml/settingslib_button_shape_state_list.xml b/packages/SettingsLib/SettingsTheme/res/xml/settingslib_button_shape_state_list.xml new file mode 100644 index 000000000000..defedb3ee477 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/xml/settingslib_button_shape_state_list.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 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. + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto"> + <item android:state_checkable="true" android:state_pressed="true" + app:shapeAppearance="?attr/containerShapePressed"/> + <item android:state_checked="true" + app:shapeAppearance="?attr/containerShapeChecked"/> + <item app:shapeAppearance="?attr/containerShapeDefault"/> +</selector>
\ No newline at end of file diff --git a/packages/SettingsLib/SettingsTheme/res/xml/settingslib_inner_corner_size_state_list.xml b/packages/SettingsLib/SettingsTheme/res/xml/settingslib_inner_corner_size_state_list.xml new file mode 100644 index 000000000000..6e49c7c69931 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/xml/settingslib_inner_corner_size_state_list.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 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. + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto"> + <item android:state_checkable="true" android:state_pressed="true" + app:cornerSize="4dp"/> + <item android:state_checked="true" + app:cornerSize="50%"/> + <item app:cornerSize="8dp"/> +</selector>
\ No newline at end of file diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt index 9bc334391ef9..068df8e31ed0 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt @@ -112,6 +112,7 @@ import androidx.compose.runtime.setValue import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester @@ -759,10 +760,12 @@ fun calculateWidgetSize( private fun HorizontalGridWrapper( minContentPadding: PaddingValues, gridState: LazyGridState, + dragDropState: GridDragDropState?, setContentOffset: (offset: Offset) -> Unit, modifier: Modifier = Modifier, content: LazyGridScope.(sizeInfo: SizeInfo?) -> Unit, ) { + val isDragging = dragDropState?.draggingItemKey != null if (communalResponsiveGrid()) { val flingBehavior = rememberSnapFlingBehavior(lazyGridState = gridState, snapPosition = SnapPosition.Start) @@ -775,6 +778,10 @@ private fun HorizontalGridWrapper( minHorizontalArrangement = Dimensions.ItemSpacing, minVerticalArrangement = Dimensions.ItemSpacing, setContentOffset = setContentOffset, + // Temporarily disable user gesture scrolling while dragging a widget to prevent + // conflicts between the drag and scroll gestures. Programmatic scrolling remains + // enabled to allow dragging a widget beyond the visible boundaries. + userScrollEnabled = !isDragging, content = content, ) } else { @@ -793,6 +800,10 @@ private fun HorizontalGridWrapper( contentPadding = minContentPadding, horizontalArrangement = Arrangement.spacedBy(Dimensions.ItemSpacing), verticalArrangement = Arrangement.spacedBy(Dimensions.ItemSpacing), + // Temporarily disable user gesture scrolling while dragging a widget to prevent + // conflicts between the drag and scroll gestures. Programmatic scrolling remains + // enabled to allow dragging a widget beyond the visible boundaries. + userScrollEnabled = !isDragging, ) { content(null) } @@ -862,6 +873,7 @@ private fun BoxScope.CommunalHubLazyGrid( HorizontalGridWrapper( modifier = gridModifier, gridState = gridState, + dragDropState = dragDropState, minContentPadding = minContentPadding, setContentOffset = setContentOffset, ) { sizeInfo -> @@ -1708,23 +1720,29 @@ private fun Umo( private fun UmoLegacy(viewModel: BaseCommunalViewModel, modifier: Modifier = Modifier) { AndroidView( modifier = - modifier.pointerInput(Unit) { - detectHorizontalDragGestures { change, _ -> - change.consume() - val upTime = SystemClock.uptimeMillis() - val event = - MotionEvent.obtain( - upTime, - upTime, - MotionEvent.ACTION_MOVE, - change.position.x, - change.position.y, - 0, - ) - viewModel.mediaHost.hostView.dispatchTouchEvent(event) - event.recycle() - } - }, + modifier + .clip( + shape = + RoundedCornerShape(dimensionResource(system_app_widget_background_radius)) + ) + .background(MaterialTheme.colorScheme.primary) + .pointerInput(Unit) { + detectHorizontalDragGestures { change, _ -> + change.consume() + val upTime = SystemClock.uptimeMillis() + val event = + MotionEvent.obtain( + upTime, + upTime, + MotionEvent.ACTION_MOVE, + change.position.x, + change.position.y, + 0, + ) + viewModel.mediaHost.hostView.dispatchTouchEvent(event) + event.recycle() + } + }, factory = { _ -> viewModel.mediaHost.hostView.apply { layoutParams = diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt index 957fdf7bcd8f..58bfd08dfdbc 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt @@ -340,11 +340,12 @@ fun ContentScope.NotificationScrollingStack( // set the bounds to null when the scrim disappears DisposableEffect(Unit) { onDispose { viewModel.onScrimBoundsChanged(null) } } - val minScrimTop = with(density) { ShadeHeader.Dimensions.CollapsedHeight.toPx() } + // Top position if the scrim, when it is fully expanded. + val minScrimTop = ShadeHeader.Dimensions.CollapsedHeight // The minimum offset for the scrim. The scrim is considered fully expanded when it // is at this offset. - val minScrimOffset: () -> Float = { minScrimTop - maxScrimTop() } + val minScrimOffset: () -> Float = { with(density) { minScrimTop.toPx() } - maxScrimTop() } // The height of the scrim visible on screen when it is in its resting (collapsed) state. val minVisibleScrimHeight: () -> Float = { @@ -563,6 +564,7 @@ fun ContentScope.NotificationScrollingStack( } .thenIf(shouldShowScrim) { Modifier.background(scrimBackgroundColor) } .thenIf(shouldFillMaxSize) { Modifier.fillMaxSize() } + .thenIf(supportNestedScrolling) { Modifier.padding(bottom = minScrimTop) } .debugBackground(viewModel, DEBUG_BOX_COLOR) ) { Column( diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt index a53c6b29338f..aefe83b781e0 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt @@ -239,7 +239,7 @@ private fun SceneScope.ShadeScene( modifier = modifier, shadeSession = shadeSession, ) - is ShadeMode.Dual -> error("Dual shade is not yet implemented!") + is ShadeMode.Dual -> error("Dual shade is implemented separately as an overlay.") } } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt index 8777ff924bc1..495fdafad26d 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt @@ -434,7 +434,8 @@ private class AnimatedStateImpl<T, Delta>( if (element != null) { layoutImpl.elements[element]?.let { element -> elementState( - layoutImpl.state.transitionStates, + listOf(layoutImpl.state.transitionStates), + elementKey = element.key, isInContent = { it in element.stateByContent }, ) as? TransitionState.Transition diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt index 16b4322411ac..b0d9fcd4344b 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt @@ -45,7 +45,11 @@ import androidx.compose.ui.platform.testTag import androidx.compose.ui.unit.Constraints import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.round +import androidx.compose.ui.util.fastAll +import androidx.compose.ui.util.fastAny import androidx.compose.ui.util.fastCoerceIn +import androidx.compose.ui.util.fastForEach +import androidx.compose.ui.util.fastForEachIndexed import androidx.compose.ui.util.fastForEachReversed import androidx.compose.ui.util.lerp import com.android.compose.animation.scene.content.Content @@ -92,7 +96,17 @@ internal class Element(val key: ElementKey) { /** The last and target state of this element in a given content. */ @Stable - class State(val content: ContentKey) { + class State( + /** + * A list of contents where this element state finds itself in. The last content is the + * content of the STL which is actually responsible to compose and place this element. The + * other contents (if any) are the ancestors. The ancestors do not actually place this + * element but the element is part of the ancestors scene as part of a NestedSTL. The state + * can be accessed by ancestor transitions to read the properties of this element to compute + * transformations. + */ + val contents: List<ContentKey> + ) { /** * The *target* state of this element in this content, i.e. the state of this element when * we are idle on this content. @@ -158,7 +172,8 @@ internal fun Modifier.element( // layout/drawing. // TODO(b/341072461): Revert this and read the current transitions in ElementNode directly once // we can ensure that SceneTransitionLayoutImpl will compose new contents first. - val currentTransitionStates = layoutImpl.state.transitionStates + val currentTransitionStates = getAllNestedTransitionStates(layoutImpl) + return thenIf(layoutImpl.state.isElevationPossible(content.key, key)) { Modifier.maybeElevateInContent(layoutImpl, content, key, currentTransitionStates) } @@ -166,11 +181,26 @@ internal fun Modifier.element( .testTag(key.testTag) } +/** + * Returns the transition states of all ancestors + the transition state of the current STL. The + * last element is the transition state of the local STL (the one with the highest nestingDepth). + * + * @return Each transition state of a STL is a List and this is a list of all the states. + */ +internal fun getAllNestedTransitionStates( + layoutImpl: SceneTransitionLayoutImpl +): List<List<TransitionState>> { + return buildList { + layoutImpl.ancestors.fastForEach { add(it.layoutImpl.state.transitionStates) } + add(layoutImpl.state.transitionStates) + } +} + private fun Modifier.maybeElevateInContent( layoutImpl: SceneTransitionLayoutImpl, content: Content, key: ElementKey, - transitionStates: List<TransitionState>, + transitionStates: List<List<TransitionState>>, ): Modifier { fun isSharedElement( stateByContent: Map<ContentKey, Element.State>, @@ -192,12 +222,12 @@ private fun Modifier.maybeElevateInContent( content.containerState, enabled = { val stateByContent = layoutImpl.elements.getValue(key).stateByContent - val state = elementState(transitionStates, isInContent = { it in stateByContent }) + val state = elementState(transitionStates, key, isInContent = { it in stateByContent }) state is TransitionState.Transition && state.transformationSpec .transformations(key, content.key) - .shared + ?.shared ?.transformation ?.elevateInContent == content.key && isSharedElement(stateByContent, state) && @@ -218,7 +248,7 @@ private fun Modifier.maybeElevateInContent( */ internal data class ElementModifier( internal val layoutImpl: SceneTransitionLayoutImpl, - private val currentTransitionStates: List<TransitionState>, + private val currentTransitionStates: List<List<TransitionState>>, internal val content: Content, internal val key: ElementKey, ) : ModifierNodeElement<ElementNode>() { @@ -232,7 +262,7 @@ internal data class ElementModifier( internal class ElementNode( private var layoutImpl: SceneTransitionLayoutImpl, - private var currentTransitionStates: List<TransitionState>, + private var currentTransitionStates: List<List<TransitionState>>, private var content: Content, private var key: ElementKey, ) : Modifier.Node(), DrawModifierNode, ApproachLayoutModifierNode, TraversableNode { @@ -257,10 +287,15 @@ internal class ElementNode( _element = element addToRenderAuthority(element) if (!element.stateByContent.contains(content.key)) { - val elementState = Element.State(content.key) + val contents = buildList { + layoutImpl.ancestors.fastForEach { add(it.inContent) } + add(content.key) + } + + val elementState = Element.State(contents) element.stateByContent[content.key] = elementState - layoutImpl.ancestorContentKeys.forEach { element.stateByContent[it] = elementState } + layoutImpl.ancestors.fastForEach { element.stateByContent[it.inContent] = elementState } } } @@ -273,7 +308,7 @@ internal class ElementNode( // this element was composed multiple times in the same content. val nCodeLocations = stateInContent.nodes.size if (nCodeLocations != 1 || !stateInContent.nodes.contains(this@ElementNode)) { - error("$key was composed $nCodeLocations times in ${stateInContent.content}") + error("$key was composed $nCodeLocations times in ${stateInContent.contents}") } } } @@ -288,12 +323,12 @@ internal class ElementNode( } private fun addToRenderAuthority(element: Element) { - val nestingDepth = layoutImpl.ancestorContentKeys.size + val nestingDepth = layoutImpl.ancestors.size element.renderAuthority[nestingDepth] = content.key } private fun removeFromRenderAuthority() { - val nestingDepth = layoutImpl.ancestorContentKeys.size + val nestingDepth = layoutImpl.ancestors.size if (element.renderAuthority[nestingDepth] == content.key) { element.renderAuthority.remove(nestingDepth) } @@ -305,7 +340,7 @@ internal class ElementNode( fun update( layoutImpl: SceneTransitionLayoutImpl, - currentTransitionStates: List<TransitionState>, + currentTransitionStates: List<List<TransitionState>>, content: Content, key: ElementKey, ) { @@ -326,7 +361,7 @@ internal class ElementNode( override fun isMeasurementApproachInProgress(lookaheadSize: IntSize): Boolean { // TODO(b/324191441): Investigate whether making this check more complex (checking if this // element is shared or transformed) would lead to better performance. - return layoutImpl.state.isTransitioning() + return isAnyStateTransitioning() } override fun Placeable.PlacementScope.isPlacementApproachInProgress( @@ -334,7 +369,12 @@ internal class ElementNode( ): Boolean { // TODO(b/324191441): Investigate whether making this check more complex (checking if this // element is shared or transformed) would lead to better performance. - return layoutImpl.state.isTransitioning() + return isAnyStateTransitioning() + } + + private fun isAnyStateTransitioning(): Boolean { + return layoutImpl.state.isTransitioning() || + layoutImpl.ancestors.fastAny { it.layoutImpl.state.isTransitioning() } } @ExperimentalComposeUiApi @@ -372,7 +412,7 @@ internal class ElementNode( // This is the case if for example a transition between two overlays is ongoing where // sharedElement isn't part of either but the element is still rendered as part of // the underlying scene that is currently not being transitioned. - val currentState = currentTransitionStates.last() + val currentState = currentTransitionStates.last().last() val shouldPlaceInThisContent = elementContentWhenIdle( layoutImpl, @@ -448,7 +488,7 @@ internal class ElementNode( element, transition, contentValue = { it.targetOffset }, - transformation = { it.offset }, + transformation = { it?.offset }, currentValue = { currentOffset }, isSpecified = { it != Offset.Unspecified }, ::lerp, @@ -592,8 +632,7 @@ internal class ElementNode( } } - pruneForContent(stateInContent.content) - layoutImpl.ancestorContentKeys.forEach { content -> pruneForContent(content) } + stateInContent.contents.fastForEach { pruneForContent(it) } } } } @@ -602,9 +641,10 @@ internal class ElementNode( private fun elementState( layoutImpl: SceneTransitionLayoutImpl, element: Element, - transitionStates: List<TransitionState>, + transitionStates: List<List<TransitionState>>, ): TransitionState? { - val state = elementState(transitionStates, isInContent = { it in element.stateByContent }) + val state = + elementState(transitionStates, element.key, isInContent = { it in element.stateByContent }) val transition = state as? TransitionState.Transition val previousTransition = element.lastTransition @@ -625,23 +665,48 @@ private fun elementState( } internal inline fun elementState( - transitionStates: List<TransitionState>, + transitionStates: List<List<TransitionState>>, + elementKey: ElementKey, isInContent: (ContentKey) -> Boolean, ): TransitionState? { - val lastState = transitionStates.last() - if (lastState is TransitionState.Idle) { - check(transitionStates.size == 1) - return lastState - } + // transitionStates is a list of all ancestor transition states + transitionState of the local + // STL. By traversing the list in normal order we by default prioritize the transitionState of + // the highest ancestor if it is running and has a transformation for this element. + transitionStates.fastForEachIndexed { index, states -> + if (index < transitionStates.size - 1) { + // Check if any ancestor runs a transition that has a transformation for the element + states.fastForEachReversed { state -> + if ( + state is TransitionState.Transition && + (state.transformationSpec.hasTransformation( + elementKey, + state.fromContent, + ) || + state.transformationSpec.hasTransformation(elementKey, state.toContent)) + ) { + return state + } + } + } else { + // the last state of the list, is the state of the local STL + val lastState = states.last() + if (lastState is TransitionState.Idle) { + check(states.size == 1) + return lastState + } - // Find the last transition with a content that contains the element. - transitionStates.fastForEachReversed { state -> - val transition = state as TransitionState.Transition - if (isInContent(transition.fromContent) || isInContent(transition.toContent)) { - return transition + // Find the last transition with a content that contains the element. + states.fastForEachReversed { state -> + val transition = state as TransitionState.Transition + if (isInContent(transition.fromContent) || isInContent(transition.toContent)) { + return transition + } + } } } - + // We are running a transition where both from and to don't contain the element. The element + // may still be rendered as e.g. it can be part of a idle scene where two overlays are currently + // transitioning above it. return null } @@ -706,7 +771,7 @@ private fun prepareInterruption( stateInContent.alphaInterruptionDelta = 0f stateInContent.scaleInterruptionDelta = Scale.Zero - if (!shouldPlaceElement(layoutImpl, stateInContent.content, element, transition)) { + if (!shouldPlaceElement(layoutImpl, stateInContent.contents.last(), element, transition)) { stateInContent.offsetBeforeInterruption = Offset.Unspecified stateInContent.alphaBeforeInterruption = Element.AlphaUnspecified stateInContent.scaleBeforeInterruption = Scale.Unspecified @@ -720,7 +785,7 @@ private fun prepareInterruption( } /** - * Reconcile the state of [element] in the formContent and toContent of [transition] so that the + * Reconcile the state of [element] in the fromContent and toContent of [transition] so that the * values before interruption have their expected values, taking shared transitions into account. * * @return the unique state this element had during [transition], `null` if it had multiple @@ -878,7 +943,7 @@ private inline fun <T> setPlacementInterruptionDelta( // If the element is shared, also set the delta on the other content so that it is used by that // content if we start overscrolling it and change the content where the element is placed. val otherContent = - if (stateInContent.content == transition.fromContent) transition.toContent + if (stateInContent.contents.last() == transition.fromContent) transition.toContent else transition.fromContent val otherContentState = element.stateByContent[otherContent] ?: return if (isSharedElementEnabled(element.key, transition)) { @@ -916,7 +981,8 @@ private fun shouldPlaceElement( if ( content != transition.fromContent && content != transition.toContent && - (!isReplacingOverlay || content != transition.currentScene) + (!isReplacingOverlay || content != transition.currentScene) && + transitionDoesNotInvolveAncestorContent(layoutImpl, transition) ) { return false } @@ -938,6 +1004,15 @@ private fun shouldPlaceElement( return shouldPlaceSharedElement(layoutImpl, content, element.key, transition) } +private fun transitionDoesNotInvolveAncestorContent( + layoutImpl: SceneTransitionLayoutImpl, + transition: TransitionState.Transition, +): Boolean { + return layoutImpl.ancestors.fastAll { + it.inContent != transition.fromContent && it.inContent != transition.toContent + } +} + /** * Whether the element is opaque or not. * @@ -968,7 +1043,7 @@ private fun isElementOpaque( return true } - return transition.transformationSpec.transformations(element.key, content.key).alpha == null + return transition.transformationSpec.transformations(element.key, content.key)?.alpha == null } /** @@ -992,7 +1067,7 @@ private fun elementAlpha( element, transition, contentValue = { 1f }, - transformation = { it.alpha }, + transformation = { it?.alpha }, currentValue = { 1f }, isSpecified = { true }, ::lerp, @@ -1060,7 +1135,7 @@ private fun measure( element, transition, contentValue = { it.targetSize }, - transformation = { it.size }, + transformation = { it?.size }, currentValue = { measurable.measure(constraints).also { maybePlaceable = it }.size() }, isSpecified = { it != Element.SizeUnspecified }, ::lerp, @@ -1093,7 +1168,6 @@ private fun measure( ) }, ) - return measurable.measure( Constraints.fixed( interruptedSize.width.coerceAtLeast(0), @@ -1117,7 +1191,7 @@ private fun ContentDrawScope.getDrawScale( element, transition, contentValue = { Scale.Default }, - transformation = { it.drawScale }, + transformation = { it?.drawScale }, currentValue = { Scale.Default }, isSpecified = { true }, ::lerp, @@ -1205,7 +1279,8 @@ private inline fun <T> computeValue( element: Element, transition: TransitionState.Transition?, contentValue: (Element.State) -> T, - transformation: (ElementTransformations) -> TransformationWithRange<PropertyTransformation<T>>?, + transformation: + (ElementTransformations?) -> TransformationWithRange<PropertyTransformation<T>>?, currentValue: () -> T, isSpecified: (T) -> Boolean, lerp: (T, T, Float) -> T, @@ -1230,7 +1305,7 @@ private inline fun <T> computeValue( return contentValue(currentContentState) } - val currentContent = currentContentState.content + val currentContent = currentContentState.contents.last() // The element is shared: interpolate between the value in fromContent and the value in // toContent. @@ -1265,23 +1340,52 @@ private inline fun <T> computeValue( } } + // The content for which we compute the transformation. Note that this is not necessarily + // [currentContent] because [currentContent] could be a different content than the transition + // fromContent or toContent during interruptions or when a ancestor transition is running. + val content: ContentKey // Get the transformed value, i.e. the target value at the beginning (for entering elements) or // end (for leaving elements) of the transition. - val contentState = - checkNotNull( - when { - isSharedElement && currentContent == fromContent -> fromState - isSharedElement -> toState - currentSceneState != null && currentContent == transition.currentScene -> - currentSceneState - else -> fromState ?: toState + val contentState: Element.State + when { + isSharedElement -> { + content = currentContent + contentState = currentContentState + } + isAncestorTransition(layoutImpl, transition) -> { + if ( + fromState != null && + transition.transformationSpec.hasTransformation(element.key, fromContent) + ) { + content = fromContent + contentState = fromState + } else if ( + toState != null && + transition.transformationSpec.hasTransformation(element.key, toContent) + ) { + content = toContent + contentState = toState + } else { + throw IllegalStateException( + "Ancestor transition is active but no transformation " + + "spec was found. The ancestor transition should have only been selected " + + "when a transformation for that element and content was defined." + ) } - ) - - // The content for which we compute the transformation. Note that this is not necessarily - // [currentContent] because [currentContent] could be a different content than the transition - // fromContent or toContent during interruptions. - val content = contentState.content + } + currentSceneState != null && currentContent == transition.currentScene -> { + content = currentContent + contentState = currentSceneState + } + fromState != null -> { + content = fromContent + contentState = fromState + } + else -> { + content = toContent + contentState = toState!! + } + } val transformationWithRange = transformation(transition.transformationSpec.transformations(element.key, content)) @@ -1437,6 +1541,8 @@ private inline fun <T> computeValue( when { content == toContent -> true content == fromContent -> false + isAncestorTransition(layoutImpl, transition) -> + isEnteringAncestorTransition(layoutImpl, transition) content == transition.currentScene -> toState == null else -> content == toContent } @@ -1447,6 +1553,22 @@ private inline fun <T> computeValue( } } +private fun isAncestorTransition( + layoutImpl: SceneTransitionLayoutImpl, + transition: TransitionState.Transition, +): Boolean { + return layoutImpl.ancestors.fastAny { + it.inContent == transition.fromContent || it.inContent == transition.toContent + } +} + +private fun isEnteringAncestorTransition( + layoutImpl: SceneTransitionLayoutImpl, + transition: TransitionState.Transition +): Boolean { + return layoutImpl.ancestors.fastAny { it.inContent == transition.toContent } +} + private inline fun <T> PropertyTransformation<T>.requireInterpolatedTransformation( element: Element, transition: TransitionState.Transition, diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt index 388456e8893a..c10a48567b22 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt @@ -163,7 +163,7 @@ private class MovableElementScopeImpl( // Important: Like in Modifier.element(), we read the transition states during // composition then pass them to Layout to make sure that composition sees new states // before layout and drawing. - val transitionStates = layoutImpl.state.transitionStates + val transitionStates = getAllNestedTransitionStates(layoutImpl) Layout { _, _ -> // No need to measure or place anything. val size = @@ -186,7 +186,7 @@ private fun shouldComposeMovableElement( element: MovableElementKey, ): Boolean { return when ( - val elementState = movableElementState(element, layoutImpl.state.transitionStates) + val elementState = movableElementState(element, getAllNestedTransitionStates(layoutImpl)) ) { null -> movableElementContentWhenIdle(layoutImpl, element, layoutImpl.state.transitionState) == @@ -221,10 +221,14 @@ private fun shouldComposeMoveableElement( private fun movableElementState( element: MovableElementKey, - transitionStates: List<TransitionState>, + transitionStates: List<List<TransitionState>>, ): TransitionState? { val contents = element.contentPicker.contents - return elementState(transitionStates, isInContent = { contents.contains(it) }) + return elementState( + transitionStates, + elementKey = element, + isInContent = { contents.contains(it) }, + ) } private fun movableElementContentWhenIdle( @@ -245,7 +249,7 @@ private fun placeholderContentSize( content: ContentKey, element: Element, elementKey: MovableElementKey, - transitionStates: List<TransitionState>, + transitionStates: List<List<TransitionState>>, ): IntSize { // If the content of the movable element was already composed in this scene before, use that // target size. diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt index ce385abea627..7b30a2a475e3 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt @@ -698,7 +698,7 @@ internal fun SceneTransitionLayoutForTesting( transitionInterceptionThreshold: Float = 0f, onLayoutImpl: ((SceneTransitionLayoutImpl) -> Unit)? = null, sharedElementMap: MutableMap<ElementKey, Element> = remember { mutableMapOf() }, - ancestorContentKeys: List<ContentKey> = emptyList(), + ancestors: List<Ancestor> = remember { emptyList() }, lookaheadScope: LookaheadScope? = null, builder: SceneTransitionLayoutScope.() -> Unit, ) { @@ -715,7 +715,7 @@ internal fun SceneTransitionLayoutForTesting( builder = builder, animationScope = animationScope, elements = sharedElementMap, - ancestorContentKeys = ancestorContentKeys, + ancestors = ancestors, lookaheadScope = lookaheadScope, ) .also { onLayoutImpl?.invoke(it) } @@ -738,9 +738,9 @@ internal fun SceneTransitionLayoutForTesting( "when creating it, which is not supported" ) } - if (layoutImpl.ancestorContentKeys != ancestorContentKeys) { + if (layoutImpl.ancestors != ancestors) { error( - "This SceneTransitionLayout was bound to a different ancestorContents that was " + + "This SceneTransitionLayout was bound to a different ancestors that was " + "used when creating it, which is not supported" ) } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt index e1cecc750d3d..e5bdc92b5762 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt @@ -57,6 +57,18 @@ import kotlinx.coroutines.launch /** The type for the content of movable elements. */ internal typealias MovableElementContent = @Composable (@Composable () -> Unit) -> Unit +internal data class Ancestor( + val layoutImpl: SceneTransitionLayoutImpl, + + /** + * This is the content in which the corresponding descendant of this ancestor appears in. + * + * Example: When A is the root and has two scenes SA and SB and SB contains a NestedSTL called + * B. Then A is the ancestor of B and inContent is SB. + */ + val inContent: ContentKey, +) + @Stable internal class SceneTransitionLayoutImpl( internal val state: MutableSceneTransitionLayoutStateImpl, @@ -83,16 +95,17 @@ internal class SceneTransitionLayoutImpl( internal val elements: MutableMap<ElementKey, Element> = mutableMapOf(), /** - * When this STL is a [NestedSceneTransitionLayout], this is a list of [ContentKey]s of where - * this STL is composed in within its ancestors. + * When this STL is a [NestedSceneTransitionLayout], this is a list of [Ancestor]s which + * provides a reference to the ancestor STLs and indicates where this STL is composed in within + * its ancestors. * * The root STL holds an emptyList. With each nesting level the parent is supposed to add * exactly one scene to the list, therefore the size of this list is equal to the nesting depth * of this STL. * - * This is used to know in which content of the ancestors a sharedElement appears in. + * This is used to enable transformations and shared elements across NestedSTLs. */ - internal val ancestorContentKeys: List<ContentKey> = emptyList(), + internal val ancestors: List<Ancestor> = emptyList(), lookaheadScope: LookaheadScope? = null, ) { diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt index 6479e69a2aac..756d71c1b5cf 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt @@ -266,19 +266,26 @@ internal class TransformationSpecImpl( override val distance: UserActionDistance?, override val transformationMatchers: List<TransformationMatcher>, ) : TransformationSpec { - private val cache = mutableMapOf<ElementKey, MutableMap<ContentKey, ElementTransformations>>() + private val cache = mutableMapOf<ElementKey, MutableMap<ContentKey, ElementTransformations?>>() - internal fun transformations(element: ElementKey, content: ContentKey): ElementTransformations { + internal fun transformations( + element: ElementKey, + content: ContentKey, + ): ElementTransformations? { return cache .getOrPut(element) { mutableMapOf() } .getOrPut(content) { computeTransformations(element, content) } } + internal fun hasTransformation(element: ElementKey, content: ContentKey): Boolean { + return transformations(element, content) != null + } + /** Filter [transformationMatchers] to compute the [ElementTransformations] of [element]. */ private fun computeTransformations( element: ElementKey, content: ContentKey, - ): ElementTransformations { + ): ElementTransformations? { var shared: TransformationWithRange<SharedElementTransformation>? = null var offset: TransformationWithRange<PropertyTransformation<Offset>>? = null var size: TransformationWithRange<PropertyTransformation<IntSize>>? = null @@ -338,7 +345,13 @@ internal class TransformationSpecImpl( } } - return ElementTransformations(shared, offset, size, drawScale, alpha) + return if ( + shared == null && offset == null && size == null && drawScale == null && alpha == null + ) { + null + } else { + ElementTransformations(shared, offset, size, drawScale, alpha) + } } private fun throwIfNotNull( diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SharedElement.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SharedElement.kt index 9de297f3ad5a..ed3a5cac8184 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SharedElement.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SharedElement.kt @@ -81,8 +81,9 @@ internal fun sharedElementTransformation( ): TransformationWithRange<SharedElementTransformation>? { val transformationSpec = transition.transformationSpec val sharedInFromContent = - transformationSpec.transformations(element, transition.fromContent).shared - val sharedInToContent = transformationSpec.transformations(element, transition.toContent).shared + transformationSpec.transformations(element, transition.fromContent)?.shared + val sharedInToContent = + transformationSpec.transformations(element, transition.toContent)?.shared // The sharedElement() transformation must either be null or be the same in both contents. if (sharedInFromContent != sharedInToContent) { diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt index 3716df5b5a35..8c5a72738a41 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt @@ -31,6 +31,7 @@ import androidx.compose.ui.layout.approachLayout import androidx.compose.ui.platform.testTag import androidx.compose.ui.unit.IntSize import androidx.compose.ui.zIndex +import com.android.compose.animation.scene.Ancestor import com.android.compose.animation.scene.AnimatedState import com.android.compose.animation.scene.ContentKey import com.android.compose.animation.scene.ContentScope @@ -181,16 +182,17 @@ internal class ContentScopeImpl( modifier: Modifier, builder: SceneTransitionLayoutScope.() -> Unit, ) { + val ancestors = + remember(layoutImpl, contentKey, layoutImpl.ancestors) { + layoutImpl.ancestors + Ancestor(layoutImpl, contentKey) + } SceneTransitionLayoutForTesting( state, modifier, onLayoutImpl = null, builder = builder, sharedElementMap = layoutImpl.elements, - ancestorContentKeys = - remember(layoutImpl.ancestorContentKeys, contentKey) { - layoutImpl.ancestorContentKeys + contentKey - }, + ancestors = ancestors, lookaheadScope = layoutImpl.lookaheadScope, ) } diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt index c69129b38bdd..676903274b38 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt @@ -2208,6 +2208,7 @@ class ElementTest { } @Test + @Ignore("b/363964445") fun interruption_considerPreviousUniqueState() { @Composable fun SceneScope.Foo(modifier: Modifier = Modifier) { diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/NestedElementTransformationTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/NestedElementTransformationTest.kt new file mode 100644 index 000000000000..0da422bcb696 --- /dev/null +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/NestedElementTransformationTest.kt @@ -0,0 +1,410 @@ +/* + * 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 com.android.compose.animation.scene.transformation + +import androidx.compose.animation.core.LinearEasing +import androidx.compose.animation.core.tween +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.size +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.test.SemanticsNodeInteraction +import androidx.compose.ui.test.assertPositionInRootIsEqualTo +import androidx.compose.ui.test.isNotDisplayed +import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.compose.animation.scene.ContentScope +import com.android.compose.animation.scene.ElementKey +import com.android.compose.animation.scene.MutableSceneTransitionLayoutState +import com.android.compose.animation.scene.Scale +import com.android.compose.animation.scene.SceneKey +import com.android.compose.animation.scene.SceneTransitionLayout +import com.android.compose.animation.scene.SceneTransitions +import com.android.compose.animation.scene.TestScenes +import com.android.compose.animation.scene.testNestedTransition +import com.android.compose.animation.scene.testing.lastAlphaForTesting +import com.android.compose.animation.scene.testing.lastScaleForTesting +import com.android.compose.animation.scene.transitions +import com.android.compose.test.assertSizeIsEqualTo +import com.google.common.truth.Truth.assertThat +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class NestedElementTransformationTest { + @get:Rule val rule = createComposeRule() + + private object Scenes { + val NestedSceneA = SceneKey("NestedSceneA") + val NestedSceneB = SceneKey("NestedSceneB") + val NestedNestedSceneA = SceneKey("NestedNestedSceneA") + val NestedNestedSceneB = SceneKey("NestedNestedSceneB") + } + + // Variants are named: nestingDepth + sceneNameSuffix + private val elementVariant0A = + TestElement(ElementKey("0A"), 0.dp, 0.dp, 100.dp, 100.dp, Color.Red) + private val elementVariant0B = + TestElement(ElementKey("0B"), 100.dp, 100.dp, 20.dp, 20.dp, Color.Cyan) + private val elementVariant1A = + TestElement(ElementKey("1A"), 40.dp, 80.dp, 60.dp, 20.dp, Color.Blue) + private val elementVariant1B = + TestElement(ElementKey("1B"), 80.dp, 40.dp, 140.dp, 180.dp, Color.Yellow) + private val elementVariant2A = + TestElement(ElementKey("2A"), 120.dp, 240.dp, 20.dp, 140.dp, Color.Green) + private val elementVariant2B = + TestElement(ElementKey("2B"), 200.dp, 320.dp, 40.dp, 70.dp, Color.Magenta) + + private class TestElement( + val key: ElementKey, + val x: Dp, + val y: Dp, + val width: Dp, + val height: Dp, + val color: Color = Color.Black, + val alpha: Float = 0.8f, + ) + + @Composable + private fun ContentScope.TestElement(element: TestElement) { + Box(Modifier.fillMaxSize()) { + Box( + Modifier.offset(element.x, element.y) + .element(element.key) + .size(element.width, element.height) + .background(element.color) + .alpha(element.alpha) + ) + } + } + + private fun createState( + startScene: SceneKey, + transitions: SceneTransitions = SceneTransitions.Empty, + ): MutableSceneTransitionLayoutState { + return rule.runOnUiThread { MutableSceneTransitionLayoutState(startScene, transitions) } + } + + private val threeNestedStls: + @Composable + (states: List<MutableSceneTransitionLayoutState>) -> Unit = + { states -> + SceneTransitionLayout(states[0]) { + scene(TestScenes.SceneA, content = { TestElement(elementVariant0A) }) + scene( + TestScenes.SceneB, + content = { + Box(Modifier.fillMaxSize()) { + TestElement(elementVariant0B) + NestedSceneTransitionLayout(states[1], modifier = Modifier) { + scene(Scenes.NestedSceneA) { + Box(Modifier.fillMaxSize()) { + TestElement(elementVariant1A) + NestedSceneTransitionLayout( + state = states[2], + modifier = Modifier, + ) { + scene(Scenes.NestedNestedSceneA) { + TestElement(elementVariant2A) + } + scene(Scenes.NestedNestedSceneB) { + TestElement(elementVariant2B) + } + } + } + } + scene(Scenes.NestedSceneB) { TestElement(elementVariant1B) } + } + } + }, + ) + } + } + + @Test + fun transitionInNestedNestedStl_transitionsOut() { + rule.testNestedTransition( + states = + listOf( + createState(TestScenes.SceneB), + createState(Scenes.NestedSceneA), + createState( + Scenes.NestedNestedSceneA, + transitions { + from(from = Scenes.NestedNestedSceneA, to = Scenes.NestedNestedSceneB) { + spec = tween(16 * 4, easing = LinearEasing) + translate(elementVariant2A.key, x = 100.dp, y = 50.dp) + scaleSize(elementVariant2A.key, width = 2f, height = 0.5f) + scaleDraw(elementVariant2A.key, scaleX = 4f, scaleY = 0.25f) + fade(elementVariant2A.key) + } + }, + ), + ), + transitionLayout = threeNestedStls, + changeState = { it[2].setTargetScene(Scenes.NestedNestedSceneB, this) }, + ) { + before { onElement(elementVariant2A.key).assertElementVariant(elementVariant2A) } + atAllFrames(4) { + onElement(elementVariant2A.key) + .assertPositionInRootIsEqualTo( + interpolate(elementVariant2A.x, elementVariant2A.x + 100.dp), + interpolate(elementVariant2A.y, elementVariant2A.y + 50.dp), + ) + .assertSizeIsEqualTo( + interpolate(elementVariant2A.width, elementVariant2A.width * 2f), + interpolate(elementVariant2A.height, elementVariant2A.height * 0.5f), + ) + val semanticNode = onElement(elementVariant2A.key).fetchSemanticsNode() + assertThat(semanticNode.lastAlphaForTesting).isEqualTo(interpolate(1f, 0f)) + assertThat(semanticNode.lastScaleForTesting) + .isEqualTo(interpolate(Scale(1f, 1f), Scale(4f, 0.25f))) + } + after { onElement(elementVariant2A.key).isNotDisplayed() } + } + } + + @Test + fun transitionInNestedNestedStl_transitionsIn() { + rule.testNestedTransition( + states = + listOf( + createState(TestScenes.SceneB), + createState(Scenes.NestedSceneA), + createState( + Scenes.NestedNestedSceneB, + transitions { + from(from = Scenes.NestedNestedSceneB) { + spec = tween(16 * 4, easing = LinearEasing) + translate(elementVariant2A.key, x = 100.dp, y = 50.dp) + scaleSize(elementVariant2A.key, width = 2f, height = 0.5f) + } + }, + ), + ), + transitionLayout = threeNestedStls, + changeState = { it[2].setTargetScene(Scenes.NestedNestedSceneA, this) }, + ) { + before { onElement(elementVariant2A.key).isNotDisplayed() } + atAllFrames(4) { + onElement(elementVariant2A.key) + .assertPositionInRootIsEqualTo( + interpolate(elementVariant2A.x + 100.dp, elementVariant2A.x), + interpolate(elementVariant2A.y + 50.dp, elementVariant2A.y), + ) + .assertSizeIsEqualTo( + interpolate(elementVariant2A.width * 2f, elementVariant2A.width), + interpolate(elementVariant2A.height * 0.5f, elementVariant2A.height), + ) + } + after { onElement(elementVariant2A.key).assertElementVariant(elementVariant2A) } + } + } + + @Test + fun transitionInNestedStl_elementInNestedNestedStl_transitionsIn() { + rule.testNestedTransition( + states = + listOf( + createState(TestScenes.SceneB), + createState( + Scenes.NestedSceneB, + transitions { + from(from = Scenes.NestedSceneB, to = Scenes.NestedSceneA) { + spec = tween(16 * 4, easing = LinearEasing) + translate(elementVariant2A.key, x = 100.dp, y = 50.dp) + scaleSize(elementVariant2A.key, width = 2f, height = 0.5f) + } + }, + ), + createState(Scenes.NestedNestedSceneA), + ), + transitionLayout = threeNestedStls, + changeState = { it[1].setTargetScene(Scenes.NestedSceneA, this) }, + ) { + before { onElement(elementVariant2A.key).isNotDisplayed() } + atAllFrames(4) { + onElement(elementVariant2A.key) + .assertPositionInRootIsEqualTo( + interpolate(elementVariant2A.x + 100.dp, elementVariant2A.x), + interpolate(elementVariant2A.y + 50.dp, elementVariant2A.y), + ) + .assertSizeIsEqualTo( + interpolate(elementVariant2A.width * 2f, elementVariant2A.width), + interpolate(elementVariant2A.height * 0.5f, elementVariant2A.height), + ) + } + after { onElement(elementVariant2A.key).assertElementVariant(elementVariant2A) } + } + } + + @Test + fun transitionInRootStl_elementsInAllLayers_transitionInAndOut() { + rule.testNestedTransition( + states = + listOf( + createState( + TestScenes.SceneB, + transitions { + to(to = TestScenes.SceneA) { + spec = tween(16 * 4, easing = LinearEasing) + + // transitions out + translate(elementVariant2A.key, x = 100.dp, y = 50.dp) + scaleSize(elementVariant2A.key, width = 2f, height = 0.5f) + + // transitions out + translate(elementVariant0B.key, x = 200.dp, y = 20.dp) + scaleSize(elementVariant0B.key, width = 3f, height = 0.2f) + + // transitions out + translate(elementVariant1A.key, x = 300.dp, y = 10.dp) + scaleSize(elementVariant1A.key, width = 4f, height = 0.1f) + + // transitions in + translate(elementVariant0A.key, x = 400.dp, y = 40.dp) + scaleSize(elementVariant0A.key, width = 0.5f, height = 2f) + } + }, + ), + createState(Scenes.NestedSceneA), + createState(Scenes.NestedNestedSceneA), + ), + transitionLayout = threeNestedStls, + changeState = { it[0].setTargetScene(TestScenes.SceneA, this) }, + ) { + before { + onElement(elementVariant2A.key).assertElementVariant(elementVariant2A) + onElement(elementVariant0B.key).assertElementVariant(elementVariant0B) + onElement(elementVariant1A.key).assertElementVariant(elementVariant1A) + onElement(elementVariant0A.key).isNotDisplayed() + } + atAllFrames(4) { + onElement(elementVariant2A.key) + .assertPositionInRootIsEqualTo( + interpolate(elementVariant2A.x, elementVariant2A.x + 100.dp), + interpolate(elementVariant2A.y, elementVariant2A.y + 50.dp), + ) + .assertSizeIsEqualTo( + interpolate(elementVariant2A.width, elementVariant2A.width * 2f), + interpolate(elementVariant2A.height, elementVariant2A.height * 0.5f), + ) + + onElement(elementVariant0B.key) + .assertPositionInRootIsEqualTo( + interpolate(elementVariant0B.x, elementVariant0B.x + 200.dp), + interpolate(elementVariant0B.y, elementVariant0B.y + 20.dp), + ) + .assertSizeIsEqualTo( + interpolate(elementVariant0B.width, elementVariant0B.width * 3f), + interpolate(elementVariant0B.height, elementVariant0B.height * 0.2f), + ) + + onElement(elementVariant1A.key) + .assertPositionInRootIsEqualTo( + interpolate(elementVariant1A.x, elementVariant1A.x + 300.dp), + interpolate(elementVariant1A.y, elementVariant1A.y + 10.dp), + ) + .assertSizeIsEqualTo( + interpolate(elementVariant1A.width, elementVariant1A.width * 4f), + interpolate(elementVariant1A.height, elementVariant1A.height * 0.1f), + ) + + onElement(elementVariant0A.key) + .assertPositionInRootIsEqualTo( + interpolate(elementVariant0A.x + 400.dp, elementVariant0A.x), + interpolate(elementVariant0A.y + 40.dp, elementVariant0A.y), + ) + .assertSizeIsEqualTo( + interpolate(elementVariant0A.width * 0.5f, elementVariant0A.width), + interpolate(elementVariant0A.height * 2f, elementVariant0A.height), + ) + } + after { + onElement(elementVariant2A.key).isNotDisplayed() + onElement(elementVariant0B.key).isNotDisplayed() + onElement(elementVariant1A.key).isNotDisplayed() + onElement(elementVariant0A.key).assertElementVariant(elementVariant0A) + } + } + } + + @Test + fun transitionInMultipleStls_rootIsTakingControl() { + rule.testNestedTransition( + states = + listOf( + createState( + TestScenes.SceneB, + transitions { + to(to = TestScenes.SceneA) { + spec = tween(16 * 4, easing = LinearEasing) + translate(elementVariant2A.key, x = 100.dp, y = 50.dp) + } + }, + ), + createState( + Scenes.NestedSceneA, + transitions { + to(to = Scenes.NestedSceneB) { + spec = tween(16 * 4, easing = LinearEasing) + translate(elementVariant2A.key, x = 200.dp, y = 150.dp) + } + }, + ), + createState( + Scenes.NestedNestedSceneA, + transitions { + to(to = Scenes.NestedNestedSceneB) { + spec = tween(16 * 4, easing = LinearEasing) + translate(elementVariant2A.key, x = 300.dp, y = 250.dp) + } + }, + ), + ), + transitionLayout = threeNestedStls, + changeState = { + it[2].setTargetScene(Scenes.NestedNestedSceneB, this) + it[1].setTargetScene(Scenes.NestedSceneB, this) + it[0].setTargetScene(TestScenes.SceneA, this) + }, + ) { + before { onElement(elementVariant2A.key).assertElementVariant(elementVariant2A) } + atAllFrames(4) { + onElement(elementVariant2A.key) + .assertPositionInRootIsEqualTo( + interpolate(elementVariant2A.x, elementVariant2A.x + 100.dp), + interpolate(elementVariant2A.y, elementVariant2A.y + 50.dp), + ) + } + after { onElement(elementVariant2A.key).isNotDisplayed() } + } + } + + private fun SemanticsNodeInteraction.assertElementVariant(variant: TestElement) { + assertPositionInRootIsEqualTo(variant.x, variant.y) + assertSizeIsEqualTo(variant.width, variant.height) + } +} diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/NestedSharedElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/NestedSharedElementTest.kt index c6ef8cff1a66..d8b713625681 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/NestedSharedElementTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/NestedSharedElementTest.kt @@ -61,7 +61,7 @@ class NestedSharedElementTest { val NestedNestedSceneB = SceneKey("NestedNestedSceneB") } - private val elementVariant1 = SharedElement(0.dp, 0.dp, 100.dp, 100.dp, Color.Red) + private val elementVariant1 = SharedElement(100.dp, 100.dp, 100.dp, 100.dp, Color.Red) private val elementVariant2 = SharedElement(40.dp, 80.dp, 60.dp, 20.dp, Color.Blue) private val elementVariant3 = SharedElement(80.dp, 40.dp, 140.dp, 180.dp, Color.Yellow) private val elementVariant4 = SharedElement(120.dp, 240.dp, 20.dp, 140.dp, Color.Green) @@ -223,7 +223,8 @@ class NestedSharedElementTest { // In SceneA, Foo leaves to the left edge. translate(TestElements.Foo.inScene(TestScenes.SceneA), Edge.Left, false) - // We can't reference the element inside the NestedSTL as of today + // In NestedSceneA, Foo comes in from the top edge. + translate(TestElements.Foo.inScene(Scenes.NestedSceneA), Edge.Top, false) }, ) { before { onElement(TestElements.Foo).assertElementVariant(elementVariant1) } @@ -234,6 +235,11 @@ class NestedSharedElementTest { elementVariant1.y, ) .assertSizeIsEqualTo(elementVariant1.width, elementVariant1.height) + onElement(TestElements.Foo, scene = Scenes.NestedSceneA) + .assertPositionInRootIsEqualTo( + elementVariant2.x, + interpolate(0.dp, elementVariant2.y), + ) } after { onElement(TestElements.Foo).assertElementVariant(elementVariant2) } } diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/SharedElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/SharedElementTest.kt index 47c10f5ab3a3..0dd08d92eb41 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/SharedElementTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/SharedElementTest.kt @@ -18,11 +18,13 @@ package com.android.compose.animation.scene.transformation import androidx.compose.animation.core.LinearEasing import androidx.compose.animation.core.tween +import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.size import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.test.assertIsNotDisplayed import androidx.compose.ui.test.assertPositionInRootIsEqualTo import androidx.compose.ui.test.junit4.createComposeRule @@ -47,11 +49,21 @@ class SharedElementTest { rule.testTransition( fromSceneContent = { // Foo is at (10, 50) with a size of (20, 80). - Box(Modifier.offset(10.dp, 50.dp).element(TestElements.Foo).size(20.dp, 80.dp)) + Box( + Modifier.offset(10.dp, 50.dp) + .element(TestElements.Foo) + .size(20.dp, 80.dp) + .background(Color.Red) + ) }, toSceneContent = { // Foo is at (50, 70) with a size of (10, 40). - Box(Modifier.offset(50.dp, 70.dp).element(TestElements.Foo).size(10.dp, 40.dp)) + Box( + Modifier.offset(50.dp, 70.dp) + .element(TestElements.Foo) + .size(10.dp, 40.dp) + .background(Color.Blue) + ) }, transition = { spec = tween(16 * 4, easing = LinearEasing) @@ -88,13 +100,23 @@ class SharedElementTest { fromSceneContent = { Box(Modifier.fillMaxSize()) { // Foo is at (10, 50). - Box(Modifier.offset(10.dp, 50.dp).element(TestElements.Foo)) + Box( + Modifier.offset(10.dp, 50.dp) + .element(TestElements.Foo) + .size(20.dp) + .background(Color.Red) + ) } }, toSceneContent = { Box(Modifier.fillMaxSize()) { // Foo is at (50, 60). - Box(Modifier.offset(50.dp, 60.dp).element(TestElements.Foo)) + Box( + Modifier.offset(50.dp, 60.dp) + .element(TestElements.Foo) + .size(20.dp) + .background(Color.Blue) + ) } }, transition = { @@ -104,7 +126,11 @@ class SharedElementTest { sharedElement(TestElements.Foo, enabled = false) // In SceneA, Foo leaves to the left edge. - translate(TestElements.Foo.inScene(TestScenes.SceneA), Edge.Left) + translate( + TestElements.Foo.inScene(TestScenes.SceneA), + Edge.Left, + startsOutsideLayoutBounds = false, + ) // In SceneB, Foo comes from the bottom edge. translate(TestElements.Foo.inScene(TestScenes.SceneB), Edge.Bottom) diff --git a/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestTransition.kt b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestTransition.kt index 124b61e45ed6..bc160fc02498 100644 --- a/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestTransition.kt +++ b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestTransition.kt @@ -25,6 +25,7 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset import androidx.compose.ui.semantics.SemanticsNode import androidx.compose.ui.test.SemanticsNodeInteraction import androidx.compose.ui.test.SemanticsNodeInteractionsProvider @@ -63,6 +64,9 @@ interface TransitionTestBuilder { * Important: [timestamp] must be a multiple of 16 (the duration of a frame on the JVM/Android). * There is no intermediary state between `t` and `t + 16` , so testing transitions outside of * `t = 0`, `t = 16`, `t = 32`, etc does not make sense. + * + * @param builder the builder can run assertions and is passed the CoroutineScope such that the + * test can start transitions at any desired point in time. */ fun at(timestamp: Long, builder: TransitionTestAssertionScope.() -> Unit) @@ -85,7 +89,7 @@ interface TransitionTestBuilder { } @TransitionTestDsl -interface TransitionTestAssertionScope { +interface TransitionTestAssertionScope : CoroutineScope { /** * Assert on [element]. * @@ -312,6 +316,20 @@ fun ComposeContentTestRule.testTransition( ) } +fun ComposeContentTestRule.testNestedTransition( + states: List<MutableSceneTransitionLayoutState>, + changeState: CoroutineScope.(states: List<MutableSceneTransitionLayoutState>) -> Unit, + transitionLayout: @Composable (states: List<MutableSceneTransitionLayoutState>) -> Unit, + builder: TransitionTestBuilder.() -> Unit, +) { + testTransition( + state = states[0], + changeState = { changeState(states) }, + transitionLayout = { transitionLayout(states) }, + builder = builder, + ) +} + /** Test the transition from [state] to [to]. */ fun ComposeContentTestRule.testTransition( state: MutableSceneTransitionLayoutState, @@ -319,9 +337,15 @@ fun ComposeContentTestRule.testTransition( transitionLayout: @Composable (state: MutableSceneTransitionLayoutState) -> Unit, builder: TransitionTestBuilder.() -> Unit, ) { - val test = transitionTest(builder) + lateinit var coroutineScope: CoroutineScope + setContent { + coroutineScope = rememberCoroutineScope() + transitionLayout(state) + } + val assertionScope = - object : AutoTransitionTestAssertionScope { + object : AutoTransitionTestAssertionScope, CoroutineScope by coroutineScope { + var progress = 0f override fun onElement( @@ -338,6 +362,16 @@ fun ComposeContentTestRule.testTransition( from is Int && to is Int -> lerp(from, to, progress) from is Long && to is Long -> lerp(from, to, progress) from is Dp && to is Dp -> lerp(from, to, progress) + from is Scale && to is Scale -> + Scale( + lerp(from.scaleX, to.scaleX, progress), + lerp(from.scaleY, to.scaleY, progress), + interpolate(from.pivot, to.pivot), + ) + + from is Offset && to is Offset -> + Offset(lerp(from.x, to.x, progress), lerp(from.y, to.y, progress)) + else -> throw UnsupportedOperationException( "Interpolation not supported for this type" @@ -347,14 +381,9 @@ fun ComposeContentTestRule.testTransition( } } - lateinit var coroutineScope: CoroutineScope - setContent { - coroutineScope = rememberCoroutineScope() - transitionLayout(state) - } - // Wait for the UI to be idle then test the before state. waitForIdle() + val test = transitionTest(builder) test.before(assertionScope) // Manually advance the clock to the start of the animation. diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricDisplayListenerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricDisplayListenerTest.java index 714461b715d6..daebf131062e 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricDisplayListenerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricDisplayListenerTest.java @@ -88,7 +88,7 @@ public class BiometricDisplayListenerTest extends SysuiTestCase { listener.enable(); verify(mDisplayManager).registerDisplayListener(any(), same(mHandler), - eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED)); + eq(DisplayManager.EVENT_TYPE_DISPLAY_CHANGED)); } @Test @@ -112,7 +112,7 @@ public class BiometricDisplayListenerTest extends SysuiTestCase { // The listener should register a display listener. verify(mDisplayManager).registerDisplayListener(mDisplayListenerCaptor.capture(), - same(mHandler), eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED)); + same(mHandler), eq(DisplayManager.EVENT_TYPE_DISPLAY_CHANGED)); // mOnChangedCallback should be invoked for all calls to onDisplayChanged. mDisplayListenerCaptor.getValue().onDisplayChanged(123); diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/data/repository/ScreenBrightnessDisplayManagerRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/data/repository/ScreenBrightnessDisplayManagerRepositoryTest.kt index f4cffc5d962a..2e8efdb8c84b 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/data/repository/ScreenBrightnessDisplayManagerRepositoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/data/repository/ScreenBrightnessDisplayManagerRepositoryTest.kt @@ -20,7 +20,7 @@ import android.hardware.display.BrightnessInfo import android.hardware.display.BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE import android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF import android.hardware.display.DisplayManager -import android.hardware.display.DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS +import android.hardware.display.DisplayManager.PRIVATE_EVENT_TYPE_DISPLAY_BRIGHTNESS import android.view.Display import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest @@ -120,7 +120,7 @@ class ScreenBrightnessDisplayManagerRepositoryTest : SysuiTestCase() { capture(listenerCaptor), eq(null), eq(0), - eq(PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS), + eq(PRIVATE_EVENT_TYPE_DISPLAY_BRIGHTNESS), ) val newBrightness = BrightnessInfo(0.6f, 0.3f, 0.9f) @@ -159,7 +159,7 @@ class ScreenBrightnessDisplayManagerRepositoryTest : SysuiTestCase() { capture(listenerCaptor), eq(null), eq(0), - eq(PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS), + eq(PRIVATE_EVENT_TYPE_DISPLAY_BRIGHTNESS), ) changeBrightnessInfoAndNotify( diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt index e6e5665e7694..c585d5c56a4e 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt @@ -541,7 +541,7 @@ class DisplayRepositoryTest : SysuiTestCase() { connectedDisplayListener.capture(), eq(testHandler), eq(0), - eq(DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_CONNECTION_CHANGED), + eq(DisplayManager.PRIVATE_EVENT_TYPE_DISPLAY_CONNECTION_CHANGED), ) return flowValue } @@ -558,9 +558,9 @@ class DisplayRepositoryTest : SysuiTestCase() { displayListener.capture(), eq(testHandler), eq( - DisplayManager.EVENT_FLAG_DISPLAY_ADDED or - DisplayManager.EVENT_FLAG_DISPLAY_CHANGED or - DisplayManager.EVENT_FLAG_DISPLAY_REMOVED + DisplayManager.EVENT_TYPE_DISPLAY_ADDED or + DisplayManager.EVENT_TYPE_DISPLAY_CHANGED or + DisplayManager.EVENT_TYPE_DISPLAY_REMOVED ), ) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/mediarouter/data/repository/MediaRouterRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/mediarouter/data/repository/MediaRouterRepositoryTest.kt index 77be8c718b14..6ec38ba171c3 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/mediarouter/data/repository/MediaRouterRepositoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/mediarouter/data/repository/MediaRouterRepositoryTest.kt @@ -16,8 +16,9 @@ package com.android.systemui.mediarouter.data.repository -import androidx.test.filters.SmallTest +import android.media.projection.StopReason import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.Kosmos @@ -101,7 +102,7 @@ class MediaRouterRepositoryTest : SysuiTestCase() { origin = CastDevice.CastOrigin.MediaRouter, ) - underTest.stopCasting(device) + underTest.stopCasting(device, StopReason.STOP_UNKNOWN) assertThat(castController.lastStoppedDevice).isEqualTo(device) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/CastTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/CastTileTest.java index 9f12b189d76a..31a627fe0667 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/CastTileTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/CastTileTest.java @@ -20,6 +20,7 @@ import static junit.framework.Assert.assertTrue; import static junit.framework.TestCase.assertEquals; import static org.junit.Assert.assertFalse; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.same; import static org.mockito.Mockito.any; import static org.mockito.Mockito.mock; @@ -30,6 +31,7 @@ import static org.mockito.Mockito.when; import android.media.MediaRouter; import android.media.MediaRouter.RouteInfo; import android.media.projection.MediaProjectionInfo; +import android.media.projection.StopReason; import android.os.Handler; import android.service.quicksettings.Tile; import android.testing.TestableLooper; @@ -336,7 +338,8 @@ public class CastTileTest extends SysuiTestCase { mCastTile.handleClick(null /* view */); mTestableLooper.processAllMessages(); - verify(mController, times(1)).stopCasting(same(device)); + verify(mController, times(1)) + .stopCasting(same(device), eq(StopReason.STOP_QS_TILE)); } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java index 0fd7c9876f25..fc1d73b62abd 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java @@ -27,11 +27,13 @@ import static junit.framework.Assert.assertTrue; import static org.junit.Assert.assertFalse; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.Dialog; +import android.media.projection.StopReason; import android.os.Handler; import android.platform.test.flag.junit.FlagsParameterization; import android.service.quicksettings.Tile; @@ -234,7 +236,7 @@ public class ScreenRecordTileTest extends SysuiTestCase { mTile.handleClick(null /* view */); - verify(mController, times(1)).stopRecording(); + verify(mController, times(1)).stopRecording(eq(StopReason.STOP_QS_TILE)); } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt index 0b56d7b64aab..778c73fd8638 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt @@ -17,6 +17,7 @@ package com.android.systemui.qs.tiles.impl.screenrecord.domain.interactor import android.app.Dialog +import android.media.projection.StopReason import android.os.UserHandle import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest @@ -92,7 +93,7 @@ class ScreenRecordTileUserActionInteractorTest : SysuiTestCase() { underTest.handleInput(QSTileInputTestKtx.click(recordingModel)) - verify(recordingController).stopRecording() + verify(recordingController).stopRecording(eq(StopReason.STOP_QS_TILE)) } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/RecordingServiceTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/RecordingServiceTest.java index a6a1d4a05dc7..50fa9d29659d 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/RecordingServiceTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/RecordingServiceTest.java @@ -41,6 +41,7 @@ import android.app.ActivityOptions.LaunchCookie; import android.app.Notification; import android.app.NotificationManager; import android.content.Intent; +import android.media.projection.StopReason; import android.os.Handler; import android.os.RemoteException; import android.os.UserHandle; @@ -199,16 +200,16 @@ public class RecordingServiceTest extends SysuiTestCase { public void testOnSystemRequestedStop_recordingInProgress_endsRecording() throws IOException { doReturn(true).when(mController).isRecording(); - mRecordingService.onStopped(); + mRecordingService.onStopped(StopReason.STOP_UNKNOWN); - verify(mScreenMediaRecorder).end(); + verify(mScreenMediaRecorder).end(eq(StopReason.STOP_UNKNOWN)); } @Test public void testOnSystemRequestedStop_recordingInProgress_updatesState() { doReturn(true).when(mController).isRecording(); - mRecordingService.onStopped(); + mRecordingService.onStopped(StopReason.STOP_UNKNOWN); assertUpdateState(false); } @@ -218,18 +219,18 @@ public class RecordingServiceTest extends SysuiTestCase { throws IOException { doReturn(false).when(mController).isRecording(); - mRecordingService.onStopped(); + mRecordingService.onStopped(StopReason.STOP_UNKNOWN); - verify(mScreenMediaRecorder, never()).end(); + verify(mScreenMediaRecorder, never()).end(StopReason.STOP_UNKNOWN); } @Test public void testOnSystemRequestedStop_recorderEndThrowsRuntimeException_releasesRecording() throws IOException { doReturn(true).when(mController).isRecording(); - doThrow(new RuntimeException()).when(mScreenMediaRecorder).end(); + doThrow(new RuntimeException()).when(mScreenMediaRecorder).end(StopReason.STOP_UNKNOWN); - mRecordingService.onStopped(); + mRecordingService.onStopped(StopReason.STOP_UNKNOWN); verify(mScreenMediaRecorder).release(); } @@ -238,7 +239,7 @@ public class RecordingServiceTest extends SysuiTestCase { public void testOnSystemRequestedStop_whenRecordingInProgress_showsNotifications() { doReturn(true).when(mController).isRecording(); - mRecordingService.onStopped(); + mRecordingService.onStopped(StopReason.STOP_UNKNOWN); // Processing notification ArgumentCaptor<Notification> notifCaptor = ArgumentCaptor.forClass(Notification.class); @@ -271,9 +272,9 @@ public class RecordingServiceTest extends SysuiTestCase { public void testOnSystemRequestedStop_recorderEndThrowsRuntimeException_showsErrorNotification() throws IOException { doReturn(true).when(mController).isRecording(); - doThrow(new RuntimeException()).when(mScreenMediaRecorder).end(); + doThrow(new RuntimeException()).when(mScreenMediaRecorder).end(anyInt()); - mRecordingService.onStopped(); + mRecordingService.onStopped(StopReason.STOP_UNKNOWN); verify(mRecordingService).createErrorSavingNotification(any()); ArgumentCaptor<Notification> notifCaptor = ArgumentCaptor.forClass(Notification.class); @@ -289,9 +290,9 @@ public class RecordingServiceTest extends SysuiTestCase { public void testOnSystemRequestedStop_recorderEndThrowsOOMError_releasesRecording() throws IOException { doReturn(true).when(mController).isRecording(); - doThrow(new OutOfMemoryError()).when(mScreenMediaRecorder).end(); + doThrow(new OutOfMemoryError()).when(mScreenMediaRecorder).end(anyInt()); - assertThrows(Throwable.class, () -> mRecordingService.onStopped()); + assertThrows(Throwable.class, () -> mRecordingService.onStopped(StopReason.STOP_UNKNOWN)); verify(mScreenMediaRecorder).release(); } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepositoryTest.kt index aceea909e595..ade5941d010d 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepositoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepositoryTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.screenrecord.data.repository +import android.media.projection.StopReason import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -31,6 +32,7 @@ import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.eq import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.mockito.kotlin.whenever @@ -126,8 +128,8 @@ class ScreenRecordRepositoryTest : SysuiTestCase() { @Test fun stopRecording_invokesController() = testScope.runTest { - underTest.stopRecording() + underTest.stopRecording(StopReason.STOP_PRIVACY_CHIP) - verify(recordingController).stopRecording() + verify(recordingController).stopRecording(eq(StopReason.STOP_PRIVACY_CHIP)) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt index 40f13bbbf908..17076b4d7505 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt @@ -306,12 +306,13 @@ class NotifChipsViewModelTest : SysuiTestCase() { @Test @EnableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY) - fun chips_basicTime_hiddenIfAutomaticallyPromoted() = + fun chips_basicTime_timeHiddenIfAutomaticallyPromoted() = kosmos.runTest { val latest by collectLastValue(underTest.chips) val promotedContentBuilder = PromotedNotificationContentModel.Builder("notif").apply { + this.wasPromotedAutomatically = true this.time = PromotedNotificationContentModel.When( time = 6543L, @@ -334,6 +335,36 @@ class NotifChipsViewModelTest : SysuiTestCase() { } @Test + @EnableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY) + fun chips_basicTime_timeShownIfNotAutomaticallyPromoted() = + kosmos.runTest { + val latest by collectLastValue(underTest.chips) + + val promotedContentBuilder = + PromotedNotificationContentModel.Builder("notif").apply { + this.wasPromotedAutomatically = false + this.time = + PromotedNotificationContentModel.When( + time = 6543L, + mode = PromotedNotificationContentModel.When.Mode.BasicTime, + ) + } + setNotifs( + listOf( + activeNotificationModel( + key = "notif", + statusBarChipIcon = mock<StatusBarIconView>(), + promotedContent = promotedContentBuilder.build(), + ) + ) + ) + + assertThat(latest).hasSize(1) + assertThat(latest!![0]) + .isInstanceOf(OngoingActivityChipModel.Shown.ShortTimeDelta::class.java) + } + + @Test @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY) fun chips_basicTime_isShortTimeDelta() = kosmos.runTest { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/view/ChipBackgroundContainerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/view/ChipBackgroundContainerTest.kt index 5fbdfbf17df4..0c992e0b348b 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/view/ChipBackgroundContainerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/view/ChipBackgroundContainerTest.kt @@ -40,7 +40,7 @@ class ChipBackgroundContainerTest : SysuiTestCase() { allowTestableLooperAsMainThread() TestableLooper.get(this).runWithLooper { val chipView = - LayoutInflater.from(context).inflate(R.layout.ongoing_activity_chip, null) + LayoutInflater.from(context).inflate(R.layout.ongoing_activity_chip_primary, null) underTest = chipView.requireViewById(R.id.ongoing_activity_chip_background) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/view/ChipChronometerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/view/ChipChronometerTest.kt index 6f7711759603..9483f6df05f5 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/view/ChipChronometerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/view/ChipChronometerTest.kt @@ -48,7 +48,7 @@ class ChipChronometerTest : SysuiTestCase() { allowTestableLooperAsMainThread() TestableLooper.get(this).runWithLooper { val chipView = - LayoutInflater.from(mContext).inflate(R.layout.ongoing_activity_chip, null) + LayoutInflater.from(mContext).inflate(R.layout.ongoing_activity_chip_primary, null) textView = chipView.findViewById(R.id.ongoing_activity_chip_time)!! measureTextView() calculateDoesNotFixText() @@ -161,7 +161,7 @@ class ChipChronometerTest : SysuiTestCase() { private fun measureTextView() { textView.measure( View.MeasureSpec.makeMeasureSpec(TEXT_VIEW_MAX_WIDTH, View.MeasureSpec.AT_MOST), - View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) + View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), ) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt index 26c6eb5dc47a..92271198cac0 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt @@ -35,6 +35,7 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder +import com.android.systemui.statusbar.notification.promoted.AutomaticPromotionCoordinator.Companion.EXTRA_WAS_AUTOMATICALLY_PROMOTED import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.Style import com.android.systemui.testKosmos @@ -110,6 +111,26 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() { @Test @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME) + fun extractContent_wasPromotedAutomatically_false() { + val entry = createEntry { extras.putBoolean(EXTRA_WAS_AUTOMATICALLY_PROMOTED, false) } + + val content = extractContent(entry) + + assertThat(content!!.wasPromotedAutomatically).isFalse() + } + + @Test + @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME) + fun extractContent_wasPromotedAutomatically_true() { + val entry = createEntry { extras.putBoolean(EXTRA_WAS_AUTOMATICALLY_PROMOTED, true) } + + val content = extractContent(entry) + + assertThat(content!!.wasPromotedAutomatically).isTrue() + } + + @Test + @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME) @DisableFlags(android.app.Flags.FLAG_API_RICH_ONGOING) fun extractContent_apiFlagOff_shortCriticalTextNotExtracted() { val entry = createEntry { setShortCriticalText(TEST_SHORT_CRITICAL_TEXT) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaListenerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaListenerTest.kt index f76ee5e3ebc9..cd3539d6b9a5 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaListenerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaListenerTest.kt @@ -114,7 +114,8 @@ class OngoingCallControllerViaListenerTest : SysuiTestCase() { fun setUp() { allowTestableLooperAsMainThread() TestableLooper.get(this).runWithLooper { - chipView = LayoutInflater.from(mContext).inflate(R.layout.ongoing_activity_chip, null) + chipView = + LayoutInflater.from(mContext).inflate(R.layout.ongoing_activity_chip_primary, null) } MockitoAnnotations.initMocks(this) @@ -498,7 +499,7 @@ class OngoingCallControllerViaListenerTest : SysuiTestCase() { lateinit var newChipView: View TestableLooper.get(this).runWithLooper { newChipView = - LayoutInflater.from(mContext).inflate(R.layout.ongoing_activity_chip, null) + LayoutInflater.from(mContext).inflate(R.layout.ongoing_activity_chip_primary, null) } // Change the chip view associated with the controller. diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaRepoTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaRepoTest.kt index 647b5f86fcee..cf512cdee800 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaRepoTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaRepoTest.kt @@ -29,7 +29,6 @@ import android.widget.LinearLayout import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.Flags.FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON -import com.android.systemui.Flags.FLAG_STATUS_BAR_CHIPS_MODERNIZATION import com.android.systemui.Flags.FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS import com.android.systemui.Flags.FLAG_STATUS_BAR_USE_REPOS_FOR_CALL_CHIP import com.android.systemui.SysuiTestCase @@ -104,7 +103,8 @@ class OngoingCallControllerViaRepoTest : SysuiTestCase() { fun setUp() { allowTestableLooperAsMainThread() TestableLooper.get(this).runWithLooper { - chipView = LayoutInflater.from(mContext).inflate(R.layout.ongoing_activity_chip, null) + chipView = + LayoutInflater.from(mContext).inflate(R.layout.ongoing_activity_chip_primary, null) } whenever(mockStatusBarWindowControllerStore.defaultDisplay) @@ -134,12 +134,7 @@ class OngoingCallControllerViaRepoTest : SysuiTestCase() { testScope.runCurrent() reset(mockOngoingCallListener) - whenever( - mockIActivityManager.getUidProcessState( - eq(CALL_UID), - any(), - ) - ) + whenever(mockIActivityManager.getUidProcessState(eq(CALL_UID), any())) .thenReturn(PROC_STATE_INVISIBLE) } @@ -225,38 +220,18 @@ class OngoingCallControllerViaRepoTest : SysuiTestCase() { @Test fun notifRepoHasOngoingCallNotifThenScreeningNotif_listenerNotifiedTwice() { - setNotifOnRepo( - activeNotificationModel( - key = "notif", - callType = CallType.Ongoing, - ) - ) + setNotifOnRepo(activeNotificationModel(key = "notif", callType = CallType.Ongoing)) - setNotifOnRepo( - activeNotificationModel( - key = "notif", - callType = CallType.Screening, - ) - ) + setNotifOnRepo(activeNotificationModel(key = "notif", callType = CallType.Screening)) verify(mockOngoingCallListener, times(2)).onOngoingCallStateChanged(any()) } @Test fun notifRepoHasOngoingCallNotifThenScreeningNotif_repoUpdated() { - setNotifOnRepo( - activeNotificationModel( - key = "notif", - callType = CallType.Ongoing, - ) - ) + setNotifOnRepo(activeNotificationModel(key = "notif", callType = CallType.Ongoing)) - setNotifOnRepo( - activeNotificationModel( - key = "notif", - callType = CallType.Screening, - ) - ) + setNotifOnRepo(activeNotificationModel(key = "notif", callType = CallType.Screening)) assertThat(ongoingCallRepository.ongoingCallState.value) .isInstanceOf(OngoingCallModel.NoCall::class.java) @@ -289,7 +264,7 @@ class OngoingCallControllerViaRepoTest : SysuiTestCase() { chipView.measure( View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), - View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) + View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), ) assertThat(chipView.findViewById<View>(R.id.ongoing_activity_chip_time)?.measuredWidth) @@ -309,7 +284,7 @@ class OngoingCallControllerViaRepoTest : SysuiTestCase() { chipView.measure( View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), - View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) + View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), ) assertThat(chipView.findViewById<View>(R.id.ongoing_activity_chip_time)?.measuredWidth) @@ -323,11 +298,7 @@ class OngoingCallControllerViaRepoTest : SysuiTestCase() { // Re-create the notification each time so that it's considered a different object and // will re-trigger the whole flow. setNotifOnRepo( - activeNotificationModel( - key = "notif$i", - callType = CallType.Ongoing, - whenTime = 44, - ) + activeNotificationModel(key = "notif$i", callType = CallType.Ongoing, whenTime = 44) ) } @@ -337,12 +308,7 @@ class OngoingCallControllerViaRepoTest : SysuiTestCase() { /** Regression test for b/216248574. */ @Test fun repoHasCallNotif_getUidProcessStateThrowsException_noCrash() { - whenever( - mockIActivityManager.getUidProcessState( - eq(CALL_UID), - any(), - ) - ) + whenever(mockIActivityManager.getUidProcessState(eq(CALL_UID), any())) .thenThrow(SecurityException()) // No assert required, just check no crash @@ -352,14 +318,7 @@ class OngoingCallControllerViaRepoTest : SysuiTestCase() { /** Regression test for b/216248574. */ @Test fun repoHasCallNotif_registerUidObserverThrowsException_noCrash() { - whenever( - mockIActivityManager.registerUidObserver( - any(), - any(), - any(), - any(), - ) - ) + whenever(mockIActivityManager.registerUidObserver(any(), any(), any(), any())) .thenThrow(SecurityException()) // No assert required, just check no crash @@ -416,11 +375,7 @@ class OngoingCallControllerViaRepoTest : SysuiTestCase() { @Test fun hasOngoingCall_repoHasUnrelatedNotif_returnsFalse() { setNotifOnRepo( - activeNotificationModel( - key = "unrelated", - callType = CallType.None, - uid = CALL_UID, - ) + activeNotificationModel(key = "unrelated", callType = CallType.None, uid = CALL_UID) ) assertThat(controller.hasOngoingCall()).isFalse() @@ -441,20 +396,11 @@ class OngoingCallControllerViaRepoTest : SysuiTestCase() { @Test fun hasOngoingCall_repoHasCallNotifAndCallAppNotVisible_returnsTrue() { - whenever( - mockIActivityManager.getUidProcessState( - eq(CALL_UID), - any(), - ) - ) + whenever(mockIActivityManager.getUidProcessState(eq(CALL_UID), any())) .thenReturn(PROC_STATE_INVISIBLE) setNotifOnRepo( - activeNotificationModel( - key = "notif", - callType = CallType.Ongoing, - uid = CALL_UID, - ) + activeNotificationModel(key = "notif", callType = CallType.Ongoing, uid = CALL_UID) ) assertThat(controller.hasOngoingCall()).isTrue() @@ -466,11 +412,7 @@ class OngoingCallControllerViaRepoTest : SysuiTestCase() { .thenReturn(PROC_STATE_VISIBLE) setNotifOnRepo( - activeNotificationModel( - key = "notif", - callType = CallType.Ongoing, - uid = CALL_UID, - ) + activeNotificationModel(key = "notif", callType = CallType.Ongoing, uid = CALL_UID) ) assertThat(controller.hasOngoingCall()).isFalse() @@ -482,11 +424,7 @@ class OngoingCallControllerViaRepoTest : SysuiTestCase() { controller.setChipView(invalidChipView) setNotifOnRepo( - activeNotificationModel( - key = "notif", - callType = CallType.Ongoing, - uid = CALL_UID, - ) + activeNotificationModel(key = "notif", callType = CallType.Ongoing, uid = CALL_UID) ) assertThat(controller.hasOngoingCall()).isFalse() @@ -532,7 +470,7 @@ class OngoingCallControllerViaRepoTest : SysuiTestCase() { lateinit var newChipView: View TestableLooper.get(this).runWithLooper { newChipView = - LayoutInflater.from(mContext).inflate(R.layout.ongoing_activity_chip, null) + LayoutInflater.from(mContext).inflate(R.layout.ongoing_activity_chip_primary, null) } // Change the chip view associated with the controller. diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/wmshell/TestableBubbleController.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/wmshell/TestableBubbleController.java index 4a5ebd057835..aa71b84d7bbc 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/wmshell/TestableBubbleController.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/wmshell/TestableBubbleController.java @@ -25,7 +25,6 @@ import android.view.WindowManager; import com.android.internal.statusbar.IStatusBarService; import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.WindowManagerShellWrapper; import com.android.wm.shell.bubbles.BubbleController; import com.android.wm.shell.bubbles.BubbleData; import com.android.wm.shell.bubbles.BubbleDataRepository; @@ -33,6 +32,8 @@ import com.android.wm.shell.bubbles.BubbleLogger; import com.android.wm.shell.bubbles.BubblePositioner; import com.android.wm.shell.bubbles.properties.BubbleProperties; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.DisplayImeController; +import com.android.wm.shell.common.DisplayInsetsController; import com.android.wm.shell.common.FloatingContentCoordinator; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; @@ -62,7 +63,8 @@ public class TestableBubbleController extends BubbleController { BubbleDataRepository dataRepository, IStatusBarService statusBarService, WindowManager windowManager, - WindowManagerShellWrapper windowManagerShellWrapper, + DisplayInsetsController displayInsetsController, + DisplayImeController displayImeController, UserManager userManager, LauncherApps launcherApps, BubbleLogger bubbleLogger, @@ -81,8 +83,8 @@ public class TestableBubbleController extends BubbleController { BubbleProperties bubbleProperties) { super(context, shellInit, shellCommandHandler, shellController, data, Runnable::run, floatingContentCoordinator, dataRepository, statusBarService, windowManager, - windowManagerShellWrapper, userManager, launcherApps, bubbleLogger, - taskStackListener, shellTaskOrganizer, positioner, displayController, + displayInsetsController, displayImeController, userManager, launcherApps, + bubbleLogger, taskStackListener, shellTaskOrganizer, positioner, displayController, oneHandedOptional, dragAndDropController, shellMainExecutor, shellMainHandler, new SyncExecutor(), taskViewTransitions, transitions, syncQueue, wmService, bubbleProperties); diff --git a/packages/SystemUI/res/layout/ongoing_activity_chip.xml b/packages/SystemUI/res/layout/ongoing_activity_chip_content.xml index 51217d4e27bd..6f42286d9fac 100644 --- a/packages/SystemUI/res/layout/ongoing_activity_chip.xml +++ b/packages/SystemUI/res/layout/ongoing_activity_chip_content.xml @@ -13,17 +13,11 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> -<!-- Have the wrapper frame layout match the parent height so that we get a larger touch area for - the chip. --> -<FrameLayout + +<merge xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:layout_gravity="center_vertical|start" - android:layout_marginStart="5dp" -> - <!-- TODO(b/332662551): Update this content description when this supports more than just - phone calls. --> + > + <com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer android:id="@+id/ongoing_activity_chip_background" android:layout_width="wrap_content" @@ -39,7 +33,7 @@ <ImageView android:src="@*android:drawable/ic_phone" android:id="@+id/ongoing_activity_chip_icon" - android:contentDescription="@string/ongoing_phone_call_content_description" + android:contentDescription="@string/ongoing_call_content_description" android:layout_width="@dimen/ongoing_activity_chip_icon_size" android:layout_height="@dimen/ongoing_activity_chip_icon_size" android:tint="?android:attr/colorPrimary" @@ -72,4 +66,4 @@ /> </com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer> -</FrameLayout> +</merge> diff --git a/packages/SystemUI/res/layout/ongoing_activity_chip_primary.xml b/packages/SystemUI/res/layout/ongoing_activity_chip_primary.xml new file mode 100644 index 000000000000..114fe6c0d849 --- /dev/null +++ b/packages/SystemUI/res/layout/ongoing_activity_chip_primary.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (C) 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. + --> + +<FrameLayout + style="@style/StatusBar.Chip.RootView"> + + <include layout="@layout/ongoing_activity_chip_content" /> + +</FrameLayout> + diff --git a/packages/SystemUI/res/layout/ongoing_activity_chip_secondary.xml b/packages/SystemUI/res/layout/ongoing_activity_chip_secondary.xml new file mode 100644 index 000000000000..81a782236a49 --- /dev/null +++ b/packages/SystemUI/res/layout/ongoing_activity_chip_secondary.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (C) 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. + --> + +<com.android.systemui.statusbar.chips.ui.view.SecondaryOngoingActivityChip + style="@style/StatusBar.Chip.RootView"> + + <include layout="@layout/ongoing_activity_chip_content" /> + +</com.android.systemui.statusbar.chips.ui.view.SecondaryOngoingActivityChip> + diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml index 1f4dea91db01..e4da4729ad0d 100644 --- a/packages/SystemUI/res/layout/status_bar.xml +++ b/packages/SystemUI/res/layout/status_bar.xml @@ -103,10 +103,10 @@ android:gravity="center_vertical|start" /> - <include layout="@layout/ongoing_activity_chip" + <include layout="@layout/ongoing_activity_chip_primary" android:id="@+id/ongoing_activity_chip_primary"/> - <include layout="@layout/ongoing_activity_chip" + <include layout="@layout/ongoing_activity_chip_secondary" android:id="@+id/ongoing_activity_chip_secondary" android:visibility="gone"/> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 70a94f9f9a59..6994a55cdbcd 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -782,6 +782,13 @@ <!-- The top margin for the notification children container in its non-expanded form. --> <dimen name="notification_children_container_margin_top">48dp</dimen> + <!-- The spacing between the notification children container in its non-expanded form, and the + header text above it, scaling with text size. This value is chosen so that, taking into + account the text spacing for both the text in the top line and the text in the container, + the distance between them is 4dp with the default screen configuration (and will grow + accordingly for larger font sizes). --> + <dimen name="notification_2025_children_container_margin_top">@*android:dimen/notification_2025_content_margin_top</dimen> + <!-- The height of the gap between adjacent notification sections. --> <dimen name="notification_section_divider_height">@dimen/notification_side_paddings</dimen> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 399c2d60094d..cd37c22c8bc3 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -3367,8 +3367,8 @@ <!-- Accessibility announcement to inform user to unlock using the fingerprint sensor [CHAR LIMIT=NONE] --> <string name="accessibility_fingerprint_bouncer">Authentication required. Touch the fingerprint sensor to authenticate.</string> - <!-- Content description for a chip in the status bar showing that the user is currently on a phone call. [CHAR LIMIT=NONE] --> - <string name="ongoing_phone_call_content_description">Ongoing phone call</string> + <!-- Content description for a chip in the status bar showing that the user is currently on a call. [CHAR LIMIT=NONE] --> + <string name="ongoing_call_content_description">Ongoing call</string> <!-- Provider Model: Default title of the mobile network in the mobile layout. [CHAR LIMIT=50] --> <string name="mobile_data_settings_title">Mobile data</string> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 71806781b98e..0503dbfab71d 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -73,6 +73,15 @@ <style name="StatusBar" /> <style name="StatusBar.Chip" /> + <style name="StatusBar.Chip.RootView"> + <item name="android:layout_width">wrap_content</item> + <!-- Have the root chip view match the parent height so that we get a larger touch area for + the chip. --> + <item name="android:layout_height">match_parent</item> + <item name="android:layout_gravity">center_vertical|start</item> + <item name="android:layout_marginStart">5dp</item> + </style> + <style name="StatusBar.Chip.Text"> <item name="android:layout_width">wrap_content</item> <item name="android:layout_height">wrap_content</item> diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeSlider.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeSlider.java index 92338ef3773c..1a068c4229c9 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeSlider.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeSlider.java @@ -45,16 +45,31 @@ public class AmbientVolumeSlider extends LinearLayout { new Slider.OnSliderTouchListener() { @Override public void onStartTrackingTouch(@NonNull Slider slider) { + mTrackingTouch = true; } @Override public void onStopTrackingTouch(@NonNull Slider slider) { + mTrackingTouch = false; final int value = Math.round(slider.getValue()); for (OnChangeListener listener : mChangeListeners) { listener.onValueChange(AmbientVolumeSlider.this, value); } } }; + private final Slider.OnChangeListener mSliderChangeListener = new Slider.OnChangeListener() { + @Override + public void onValueChange(@NonNull Slider slider, float value, boolean fromUser) { + if (fromUser && !mTrackingTouch) { + final int roundedValue = Math.round(value); + for (OnChangeListener listener : mChangeListeners) { + listener.onValueChange(AmbientVolumeSlider.this, roundedValue); + } + } + } + }; + private boolean mTrackingTouch = false; + public AmbientVolumeSlider(@Nullable Context context) { this(context, /* attrs= */ null); } @@ -76,6 +91,7 @@ public class AmbientVolumeSlider extends LinearLayout { mTitle = requireViewById(R.id.ambient_volume_slider_title); mSlider = requireViewById(R.id.ambient_volume_slider); mSlider.addOnSliderTouchListener(mSliderTouchListener); + mSlider.addOnChangeListener(mSliderChangeListener); } /** diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDisplayListener.kt b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDisplayListener.kt index ca479f5e6b5f..bee1f7416676 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDisplayListener.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDisplayListener.kt @@ -25,23 +25,25 @@ import com.android.app.tracing.traceSection import com.android.systemui.biometrics.BiometricDisplayListener.SensorType.Generic /** - * A listener for keeping overlays for biometric sensors aligned with the physical device - * device's screen. The [onChanged] will be dispatched on the [handler] - * whenever a relevant change to the device's configuration (orientation, fold, display change, - * etc.) may require the UI to change for the given [sensorType]. + * A listener for keeping overlays for biometric sensors aligned with the physical device device's + * screen. The [onChanged] will be dispatched on the [handler] whenever a relevant change to the + * device's configuration (orientation, fold, display change, etc.) may require the UI to change for + * the given [sensorType]. */ class BiometricDisplayListener( private val context: Context, private val displayManager: DisplayManager, private val handler: Handler, private val sensorType: SensorType = SensorType.Generic, - private val onChanged: () -> Unit + private val onChanged: () -> Unit, ) : DisplayManager.DisplayListener { private var cachedDisplayInfo = DisplayInfo() override fun onDisplayAdded(displayId: Int) {} + override fun onDisplayRemoved(displayId: Int) {} + override fun onDisplayChanged(displayId: Int) { traceSection({ "BiometricDisplayListener($sensorType)#onDisplayChanged" }) { val rotationChanged = didRotationChange() @@ -69,7 +71,7 @@ class BiometricDisplayListener( displayManager.registerDisplayListener( this, handler, - DisplayManager.EVENT_FLAG_DISPLAY_CHANGED + DisplayManager.EVENT_TYPE_DISPLAY_CHANGED, ) } @@ -81,14 +83,15 @@ class BiometricDisplayListener( /** * Type of sensor to determine what kind of display changes require layouts. * - * The [Generic] type should be used in cases where the modality can vary, such as - * biometric prompt (and this object will likely change as multi-mode auth is added). + * The [Generic] type should be used in cases where the modality can vary, such as biometric + * prompt (and this object will likely change as multi-mode auth is added). */ sealed class SensorType { data object Generic : SensorType() + data object UnderDisplayFingerprint : SensorType() - data class SideFingerprint( - val properties: FingerprintSensorPropertiesInternal - ) : SensorType() + + data class SideFingerprint(val properties: FingerprintSensorPropertiesInternal) : + SensorType() } } diff --git a/packages/SystemUI/src/com/android/systemui/brightness/data/repository/ScreenBrightnessRepository.kt b/packages/SystemUI/src/com/android/systemui/brightness/data/repository/ScreenBrightnessRepository.kt index 6c78b8b0e58a..e6d6293733d4 100644 --- a/packages/SystemUI/src/com/android/systemui/brightness/data/repository/ScreenBrightnessRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/brightness/data/repository/ScreenBrightnessRepository.kt @@ -133,7 +133,7 @@ constructor( listener, null, /* eventFlags */ 0, - DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS, + DisplayManager.PRIVATE_EVENT_TYPE_DISPLAY_BRIGHTNESS, ) awaitClose { displayManager.unregisterDisplayListener(listener) } @@ -181,10 +181,11 @@ constructor( .logDiffForTable(tableBuffer, TABLE_PREFIX_LINEAR, TABLE_COLUMN_BRIGHTNESS, null) .stateIn(applicationScope, SharingStarted.WhileSubscribed(), LinearBrightness(0f)) - override val isBrightnessOverriddenByWindow = brightnessInfo - .filterNotNull() - .map { it.isBrightnessOverrideByWindow } - .stateIn(applicationScope, SharingStarted.WhileSubscribed(), false) + override val isBrightnessOverriddenByWindow = + brightnessInfo + .filterNotNull() + .map { it.isBrightnessOverrideByWindow } + .stateIn(applicationScope, SharingStarted.WhileSubscribed(), false) override fun setTemporaryBrightness(value: LinearBrightness) { apiQueue.trySend(SetBrightnessMethod.Temporary(value)) diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt index e5acb8235a8f..d4642006e68d 100644 --- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt @@ -20,9 +20,9 @@ import android.annotation.SuppressLint import android.hardware.display.DisplayManager import android.hardware.display.DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED import android.hardware.display.DisplayManager.DisplayListener -import android.hardware.display.DisplayManager.EVENT_FLAG_DISPLAY_ADDED -import android.hardware.display.DisplayManager.EVENT_FLAG_DISPLAY_CHANGED -import android.hardware.display.DisplayManager.EVENT_FLAG_DISPLAY_REMOVED +import android.hardware.display.DisplayManager.EVENT_TYPE_DISPLAY_ADDED +import android.hardware.display.DisplayManager.EVENT_TYPE_DISPLAY_CHANGED +import android.hardware.display.DisplayManager.EVENT_TYPE_DISPLAY_REMOVED import android.os.Handler import android.util.Log import android.view.Display @@ -147,9 +147,9 @@ constructor( displayManager.registerDisplayListener( callback, backgroundHandler, - EVENT_FLAG_DISPLAY_ADDED or - EVENT_FLAG_DISPLAY_CHANGED or - EVENT_FLAG_DISPLAY_REMOVED, + EVENT_TYPE_DISPLAY_ADDED or + EVENT_TYPE_DISPLAY_CHANGED or + EVENT_TYPE_DISPLAY_REMOVED, ) awaitClose { displayManager.unregisterDisplayListener(callback) } } @@ -279,7 +279,7 @@ constructor( callback, backgroundHandler, /* eventFlags */ 0, - DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_CONNECTION_CHANGED, + DisplayManager.PRIVATE_EVENT_TYPE_DISPLAY_CONNECTION_CHANGED, ) awaitClose { displayManager.unregisterDisplayListener(callback) } } diff --git a/packages/SystemUI/src/com/android/systemui/mediarouter/data/repository/MediaRouterRepository.kt b/packages/SystemUI/src/com/android/systemui/mediarouter/data/repository/MediaRouterRepository.kt index debb667bbb15..a19c9b30f68d 100644 --- a/packages/SystemUI/src/com/android/systemui/mediarouter/data/repository/MediaRouterRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/mediarouter/data/repository/MediaRouterRepository.kt @@ -16,6 +16,7 @@ package com.android.systemui.mediarouter.data.repository +import android.media.projection.StopReason import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.log.LogBuffer @@ -40,7 +41,7 @@ interface MediaRouterRepository { val castDevices: StateFlow<List<CastDevice>> /** Stops the cast to the given device. */ - fun stopCasting(device: CastDevice) + fun stopCasting(device: CastDevice, @StopReason stopReason: Int) } @SysUISingleton @@ -67,8 +68,8 @@ constructor( .map { it.filter { device -> device.origin == CastDevice.CastOrigin.MediaRouter } } .stateIn(scope, SharingStarted.WhileSubscribed(), emptyList()) - override fun stopCasting(device: CastDevice) { - castController.stopCasting(device) + override fun stopCasting(device: CastDevice, @StopReason stopReason: Int) { + castController.stopCasting(device, stopReason) } companion object { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java index ad027b4346d0..30c2adf89e9b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java @@ -24,6 +24,7 @@ import android.annotation.NonNull; import android.app.Dialog; import android.content.Intent; import android.media.MediaRouter.RouteInfo; +import android.media.projection.StopReason; import android.os.Handler; import android.os.Looper; import android.provider.Settings; @@ -183,7 +184,7 @@ public class CastTile extends QSTileImpl<BooleanState> { }); } } else { - mController.stopCasting(activeDevices.get(0)); + mController.stopCasting(activeDevices.get(0), StopReason.STOP_QS_TILE); } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java index fc825926c374..ec8d30b01eab 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java @@ -18,6 +18,7 @@ package com.android.systemui.qs.tiles; import android.app.Dialog; import android.content.Intent; +import android.media.projection.StopReason; import android.os.Handler; import android.os.Looper; import android.service.quicksettings.Tile; @@ -226,7 +227,7 @@ public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState> } private void stopRecording() { - mController.stopRecording(); + mController.stopRecording(StopReason.STOP_QS_TILE); } private final class Callback implements RecordingController.RecordingStateChangeCallback { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractor.kt index 85aa6745e438..94534479db57 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractor.kt @@ -16,6 +16,7 @@ package com.android.systemui.qs.tiles.impl.screenrecord.domain.interactor +import android.media.projection.StopReason import android.util.Log import com.android.internal.jank.InteractionJankMonitor import com.android.systemui.animation.DialogCuj @@ -61,7 +62,9 @@ constructor( Log.d(TAG, "Cancelling countdown") withContext(backgroundContext) { recordingController.cancelCountdown() } } - is ScreenRecordModel.Recording -> screenRecordRepository.stopRecording() + is ScreenRecordModel.Recording -> { + screenRecordRepository.stopRecording(StopReason.STOP_QS_TILE) + } is ScreenRecordModel.DoingNothing -> withContext(mainContext) { showPrompt(action.expandable, user.identifier) diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java index d7463f8f0c36..9ee99e45ceeb 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java @@ -23,6 +23,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.media.projection.StopReason; import android.os.Bundle; import android.os.CountDownTimer; import android.os.Process; @@ -58,6 +59,7 @@ public class RecordingController private boolean mIsStarting; private boolean mIsRecording; private PendingIntent mStopIntent; + private @StopReason int mStopReason = StopReason.STOP_UNKNOWN; private final Bundle mInteractiveBroadcastOption; private CountDownTimer mCountDownTimer = null; private final Executor mMainExecutor; @@ -83,7 +85,7 @@ public class RecordingController new UserTracker.Callback() { @Override public void onUserChanged(int newUser, @NonNull Context userContext) { - stopRecording(); + stopRecording(StopReason.STOP_USER_SWITCH); } }; @@ -240,9 +242,11 @@ public class RecordingController } /** - * Stop the recording + * Stop the recording and sets the stop reason to be used by the RecordingService + * @param stopReason the method of the recording stopped (i.e. QS tile, status bar chip, etc.) */ - public void stopRecording() { + public void stopRecording(@StopReason int stopReason) { + mStopReason = stopReason; try { if (mStopIntent != null) { mRecordingControllerLogger.logRecordingStopped(); @@ -277,6 +281,10 @@ public class RecordingController } } + public @StopReason int getStopReason() { + return mStopReason; + } + @Override public void addCallback(@NonNull RecordingStateChangeCallback listener) { mListeners.add(listener); diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java index 8c207d13d50e..f7b52719a4f4 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java @@ -26,6 +26,7 @@ import android.content.Context; import android.content.Intent; import android.graphics.drawable.Icon; import android.media.MediaRecorder; +import android.media.projection.StopReason; import android.net.Uri; import android.os.Bundle; import android.os.Handler; @@ -78,6 +79,7 @@ public class RecordingService extends Service implements ScreenMediaRecorderList private static final String EXTRA_SHOW_TAPS = "extra_showTaps"; private static final String EXTRA_CAPTURE_TARGET = "extra_captureTarget"; private static final String EXTRA_DISPLAY_ID = "extra_displayId"; + private static final String EXTRA_STOP_REASON = "extra_stopReason"; protected static final String ACTION_START = "com.android.systemui.screenrecord.START"; protected static final String ACTION_SHOW_START_NOTIF = @@ -242,7 +244,8 @@ public class RecordingService extends Service implements ScreenMediaRecorderList // Check user ID - we may be getting a stop intent after user switch, in which case // we want to post the notifications for that user, which is NOT current user int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_ID_NOT_SPECIFIED); - stopService(userId); + int stopReason = intent.getIntExtra(EXTRA_STOP_REASON, mController.getStopReason()); + stopService(userId, stopReason); break; case ACTION_SHARE: @@ -486,11 +489,11 @@ public class RecordingService extends Service implements ScreenMediaRecorderList getTag(), notificationIdForGroup, groupNotif, currentUser); } - private void stopService() { - stopService(USER_ID_NOT_SPECIFIED); + private void stopService(@StopReason int stopReason) { + stopService(USER_ID_NOT_SPECIFIED, stopReason); } - private void stopService(int userId) { + private void stopService(int userId, @StopReason int stopReason) { if (userId == USER_ID_NOT_SPECIFIED) { userId = mUserContextTracker.getUserContext().getUserId(); } @@ -499,7 +502,7 @@ public class RecordingService extends Service implements ScreenMediaRecorderList setTapsVisible(mOriginalShowTaps); try { if (getRecorder() != null) { - getRecorder().end(); + getRecorder().end(stopReason); } saveRecording(userId); } catch (RuntimeException exception) { @@ -598,7 +601,8 @@ public class RecordingService extends Service implements ScreenMediaRecorderList * @return */ protected Intent getNotificationIntent(Context context) { - return new Intent(context, this.getClass()).setAction(ACTION_STOP_NOTIF); + return new Intent(context, this.getClass()).setAction(ACTION_STOP_NOTIF) + .putExtra(EXTRA_STOP_REASON, StopReason.STOP_HOST_APP); } private Intent getShareIntent(Context context, Uri path) { @@ -610,14 +614,17 @@ public class RecordingService extends Service implements ScreenMediaRecorderList @Override public void onInfo(MediaRecorder mr, int what, int extra) { Log.d(getTag(), "Media recorder info: " + what); - onStartCommand(getStopIntent(this), 0, 0); + // Stop due to record reaching size limits so log as stopping due to error + Intent stopIntent = getStopIntent(this); + stopIntent.putExtra(EXTRA_STOP_REASON, StopReason.STOP_ERROR); + onStartCommand(stopIntent, 0, 0); } @Override - public void onStopped() { + public void onStopped(@StopReason int stopReason) { if (mController.isRecording()) { Log.d(getTag(), "Stopping recording because the system requested the stop"); - stopService(); + stopService(stopReason); } } } diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java index 2ca0621635a7..f4455bfb7048 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java @@ -41,6 +41,7 @@ import android.media.projection.IMediaProjection; import android.media.projection.IMediaProjectionManager; import android.media.projection.MediaProjection; import android.media.projection.MediaProjectionManager; +import android.media.projection.StopReason; import android.net.Uri; import android.os.Handler; import android.os.IBinder; @@ -300,7 +301,7 @@ public class ScreenMediaRecorder extends MediaProjection.Callback { /** * End screen recording, throws an exception if stopping recording failed */ - void end() throws IOException { + void end(@StopReason int stopReason) throws IOException { Closer closer = new Closer(); // MediaRecorder might throw RuntimeException if stopped immediately after starting @@ -309,7 +310,17 @@ public class ScreenMediaRecorder extends MediaProjection.Callback { closer.register(mMediaRecorder::release); closer.register(mInputSurface::release); closer.register(mVirtualDisplay::release); - closer.register(mMediaProjection::stop); + closer.register(() -> { + if (stopReason == StopReason.STOP_UNKNOWN) { + // Attempt to call MediaProjection#stop() even if it might have already been called. + // If projection has already been stopped, then nothing will happen. Else, stop + // will be logged as a manually requested stop from host app. + mMediaProjection.stop(); + } else { + // In any other case, the stop reason is related to the recorder, so pass it on here + mMediaProjection.stop(stopReason); + } + }); closer.register(this::stopInternalAudioRecording); closer.close(); @@ -323,7 +334,7 @@ public class ScreenMediaRecorder extends MediaProjection.Callback { @Override public void onStop() { Log.d(TAG, "The system notified about stopping the projection"); - mListener.onStopped(); + mListener.onStopped(StopReason.STOP_UNKNOWN); } private void stopInternalAudioRecording() { @@ -453,7 +464,7 @@ public class ScreenMediaRecorder extends MediaProjection.Callback { * For example, this might happen when doing partial screen sharing of an app * and the app that is being captured is closed. */ - void onStopped(); + void onStopped(@StopReason int stopReason); } /** diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepository.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepository.kt index 9eeb3b9576d8..b6b8ffa11495 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepository.kt @@ -16,6 +16,7 @@ package com.android.systemui.screenrecord.data.repository +import android.media.projection.StopReason import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.screenrecord.RecordingController @@ -41,7 +42,7 @@ interface ScreenRecordRepository { val screenRecordState: Flow<ScreenRecordModel> /** Stops the recording. */ - suspend fun stopRecording() + suspend fun stopRecording(@StopReason stopReason: Int) } @SysUISingleton @@ -95,7 +96,7 @@ constructor( } } - override suspend fun stopRecording() { - withContext(bgCoroutineContext) { recordingController.stopRecording() } + override suspend fun stopRecording(@StopReason stopReason: Int) { + withContext(bgCoroutineContext) { recordingController.stopRecording(stopReason) } } } diff --git a/packages/SystemUI/src/com/android/systemui/settings/DisplayTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/DisplayTrackerImpl.kt index 60ed2de5c532..b0cbc06803df 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/DisplayTrackerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/settings/DisplayTrackerImpl.kt @@ -17,7 +17,7 @@ package com.android.systemui.settings import android.hardware.display.DisplayManager -import android.hardware.display.DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS +import android.hardware.display.DisplayManager.PRIVATE_EVENT_TYPE_DISPLAY_BRIGHTNESS import android.os.Handler import android.view.Display import androidx.annotation.GuardedBy @@ -104,7 +104,7 @@ internal constructor( displayBrightnessChangedListener, backgroundHandler, /* eventFlags */ 0, - PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS, + PRIVATE_EVENT_TYPE_DISPLAY_BRIGHTNESS, ) } brightnessCallbacks.add(DisplayTrackerDataItem(WeakReference(callback), executor)) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt index 85b50d3320dd..de08e3891902 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt @@ -132,7 +132,7 @@ constructor( private val phoneIcon = Icon.Resource( com.android.internal.R.drawable.ic_phone, - ContentDescription.Resource(R.string.ongoing_phone_call_content_description), + ContentDescription.Resource(R.string.ongoing_call_content_description), ) private val TAG = "CallVM".pad() } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/domain/interactor/MediaRouterChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/domain/interactor/MediaRouterChipInteractor.kt index b3dbf299e7cc..229cef910c6e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/domain/interactor/MediaRouterChipInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/domain/interactor/MediaRouterChipInteractor.kt @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.chips.casttootherdevice.domain.interactor +import android.media.projection.StopReason import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.log.LogBuffer @@ -65,7 +66,9 @@ constructor( /** Stops the currently active MediaRouter cast. */ fun stopCasting() { - activeCastDevice.value?.let { mediaRouterRepository.stopCasting(it) } + activeCastDevice.value?.let { + mediaRouterRepository.stopCasting(it, StopReason.STOP_PRIVACY_CHIP) + } } companion object { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt index a7dbb47bc609..bcd8cfaa5c5a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt @@ -100,14 +100,14 @@ constructor( ) } - if (Flags.promoteNotificationsAutomatically()) { + if ( + Flags.promoteNotificationsAutomatically() && + this.promotedContent.wasPromotedAutomatically + ) { // When we're promoting notifications automatically, the `when` time set on the // notification will likely just be set to the current time, which would cause the chip // to always show "now". We don't want early testers to get that experience since it's // not what will happen at launch, so just don't show any time. - // TODO(b/364653005): Only ignore the `when` time if the notification was - // *automatically* promoted (as opposed to being legitimately promoted by the - // criteria). We'll need to track that status somehow. return OngoingActivityChipModel.Shown.IconOnly(icon, colors, onClickListener) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt index f5952f4804fc..0b5e669b5fca 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.chips.screenrecord.domain.interactor +import android.media.projection.StopReason import com.android.systemui.Flags import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application @@ -140,7 +141,7 @@ constructor( /** Stops the recording. */ fun stopRecording() { - scope.launch { screenRecordRepository.stopRecording() } + scope.launch { screenRecordRepository.stopRecording(StopReason.STOP_PRIVACY_CHIP) } } companion object { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt index 0c4c1a71ccc7..c40df98f39ba 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt @@ -149,10 +149,9 @@ object OngoingActivityChipBinder { // 1. Set up the right visual params. with(iconView) { id = CUSTOM_ICON_VIEW_ID - // TODO(b/354930838): Update the content description to not include "phone" and maybe - // include the app name. + // TODO(b/354930838): For RON chips, use the app name for the content description. contentDescription = - context.resources.getString(R.string.ongoing_phone_call_content_description) + context.resources.getString(R.string.ongoing_call_content_description) tintView(iconTint) } @@ -349,14 +348,21 @@ object OngoingActivityChipBinder { } // Clickable chips need to be a minimum size for accessibility purposes, but let // non-clickable chips be smaller. - if (chipModel.onClickListener != null) { - chipBackgroundView.minimumWidth = + val minimumWidth = + if (chipModel.onClickListener != null) { chipBackgroundView.context.resources.getDimensionPixelSize( R.dimen.min_clickable_item_size ) - } else { - chipBackgroundView.minimumWidth = 0 - } + } else { + 0 + } + // The background view needs the minimum width so it only fills the area required (e.g. the + // 3-2-1 screen record countdown chip isn't tappable so it should have a small-width + // background). + chipBackgroundView.minimumWidth = minimumWidth + // The root view needs the minimum width so the second chip can hide if there isn't enough + // room for the chip -- see [SecondaryOngoingActivityChip]. + chipView.minimumWidth = minimumWidth } @IdRes private val CUSTOM_ICON_VIEW_ID = R.id.ongoing_activity_chip_custom_icon diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/view/SecondaryOngoingActivityChip.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/view/SecondaryOngoingActivityChip.kt new file mode 100644 index 000000000000..f790dc94f7f2 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/view/SecondaryOngoingActivityChip.kt @@ -0,0 +1,40 @@ +/* + * Copyright (C) 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 com.android.systemui.statusbar.chips.ui.view + +import android.content.Context +import android.util.AttributeSet +import android.widget.FrameLayout + +/** + * A custom class for the secondary ongoing activity chip. This class will completely hide itself if + * there isn't enough room for the mimimum size chip. + * + * [this.minimumWidth] must be set correctly in order for this class to work. + */ +class SecondaryOngoingActivityChip(context: Context, attrs: AttributeSet) : + FrameLayout(context, attrs) { + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec) + if (measuredWidth < this.minimumWidth) { + // There isn't enough room to fit even the minimum content required, so hide completely. + // Changing visibility ensures that the content description is not read aloud. + visibility = GONE + setMeasuredDimension(0, 0) + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AutomaticPromotionCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AutomaticPromotionCoordinator.kt index bb164848320e..395746280f6a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AutomaticPromotionCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AutomaticPromotionCoordinator.kt @@ -22,7 +22,16 @@ import com.android.systemui.statusbar.notification.collection.coordinator.dagger import javax.inject.Inject /** A coordinator that may automatically promote certain notifications. */ -interface AutomaticPromotionCoordinator : Coordinator +interface AutomaticPromotionCoordinator : Coordinator { + companion object { + /** + * An extra that should be set on notifications that were automatically promoted. Used in + * case we want to disable certain features for only automatically promoted notifications + * (but not normally promoted notifications). + */ + const val EXTRA_WAS_AUTOMATICALLY_PROMOTED = "android.wasAutomaticallyPromoted" + } +} /** A default implementation of [AutomaticPromotionCoordinator] that doesn't promote anything. */ @CoordinatorScope diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt index 7d2827666227..df2eb08e8fa4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt @@ -24,14 +24,14 @@ import android.app.Notification.EXTRA_CHRONOMETER_COUNT_DOWN import android.app.Notification.EXTRA_SUB_TEXT import android.app.Notification.EXTRA_TEXT import android.app.Notification.EXTRA_TITLE -import android.app.Notification.FLAG_PROMOTED_ONGOING import android.app.Notification.ProgressStyle import android.content.Context import com.android.systemui.dagger.SysUISingleton import com.android.systemui.shade.ShadeDisplayAware -import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.promoted.AutomaticPromotionCoordinator.Companion.EXTRA_WAS_AUTOMATICALLY_PROMOTED import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.Companion.isPromotedForStatusBarChip import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.Style import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.When import javax.inject.Inject @@ -65,12 +65,8 @@ constructor( return null } - // Notification.isPromotedOngoing checks the ui_rich_ongoing flag, but we want the status - // bar chip to be ready before all the features behind the ui_rich_ongoing flag are ready. - val isPromotedForStatusBarChip = - StatusBarNotifChips.isEnabled && (notification.flags and FLAG_PROMOTED_ONGOING) != 0 - val isPromoted = notification.isPromotedOngoing() || isPromotedForStatusBarChip - if (!isPromoted) { + // The status bar chips rely on this extractor, so take them into account for promotion. + if (!isPromotedForStatusBarChip(notification)) { logger.logExtractionSkipped(entry, "isPromotedOngoing returned false") return null } @@ -80,6 +76,8 @@ constructor( // TODO: Pitch a fit if style is unsupported or mandatory fields are missing once // FLAG_PROMOTED_ONGOING is set reliably and we're not testing status bar chips. + contentBuilder.wasPromotedAutomatically = + notification.extras.getBoolean(EXTRA_WAS_AUTOMATICALLY_PROMOTED, false) contentBuilder.skeletonSmallIcon = entry.icons.aodIcon?.sourceIcon contentBuilder.appName = notification.loadHeaderAppName(context) contentBuilder.subText = notification.subText() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt index 74809fd8622f..258d80c60cd7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt @@ -17,6 +17,8 @@ package com.android.systemui.statusbar.notification.promoted.shared.model import android.annotation.DrawableRes +import android.app.Notification +import android.app.Notification.FLAG_PROMOTED_ONGOING import android.graphics.drawable.Icon import androidx.annotation.ColorInt import com.android.internal.widget.NotificationProgressModel @@ -31,6 +33,10 @@ data class PromotedNotificationContentModel( val identity: Identity, // for all styles: + /** + * True if this notification was automatically promoted - see [AutomaticPromotionCoordinator]. + */ + val wasPromotedAutomatically: Boolean, val skeletonSmallIcon: Icon?, // TODO(b/377568176): Make into an IconModel. val appName: CharSequence?, val subText: CharSequence?, @@ -58,6 +64,7 @@ data class PromotedNotificationContentModel( val progress: NotificationProgressModel?, ) { class Builder(val key: String) { + var wasPromotedAutomatically: Boolean = false var skeletonSmallIcon: Icon? = null var appName: CharSequence? = null var subText: CharSequence? = null @@ -83,6 +90,7 @@ data class PromotedNotificationContentModel( fun build() = PromotedNotificationContentModel( identity = Identity(key, style), + wasPromotedAutomatically = wasPromotedAutomatically, skeletonSmallIcon = skeletonSmallIcon, appName = appName, subText = subText, @@ -134,5 +142,18 @@ data class PromotedNotificationContentModel( @JvmStatic fun featureFlagEnabled(): Boolean = PromotedNotificationUi.isEnabled || StatusBarNotifChips.isEnabled + + /** + * Returns true if the given notification should be considered promoted when deciding + * whether or not to show the status bar chip UI. + */ + fun isPromotedForStatusBarChip(notification: Notification): Boolean { + // Notification.isPromotedOngoing checks the ui_rich_ongoing flag, but we want the + // status bar chip to be ready before all the features behind the ui_rich_ongoing flag + // are ready. + val isPromotedForStatusBarChip = + StatusBarNotifChips.isEnabled && (notification.flags and FLAG_PROMOTED_ONGOING) != 0 + return notification.isPromotedOngoing() || isPromotedForStatusBarChip + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java index df43ff19de86..3ccf5063cd68 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.notification.row.wrapper; +import static android.app.Flags.notificationsRedesignTemplates; import static android.view.View.VISIBLE; import static com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.DEFAULT_HEADER_VISIBLE_AMOUNT; @@ -149,10 +150,15 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp } }, TRANSFORMING_VIEW_TEXT); - mFullHeaderTranslation = ctx.getResources().getDimensionPixelSize( - com.android.internal.R.dimen.notification_content_margin) - - ctx.getResources().getDimensionPixelSize( - com.android.internal.R.dimen.notification_content_margin_top); + int contentMargin = ctx.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.notification_content_margin); + int contentMarginTop = + notificationsRedesignTemplates() + ? Notification.Builder.getContentMarginTop(ctx, + com.android.internal.R.dimen.notification_2025_content_margin_top) + : ctx.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.notification_content_margin_top); + mFullHeaderTranslation = contentMargin - contentMarginTop; } @MainThread diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java index 00cd8ce87738..9fb7fad52bb6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java @@ -168,8 +168,10 @@ public class NotificationChildrenContainer extends ViewGroup mDividerHeight = res.getDimensionPixelOffset( R.dimen.notification_children_container_divider_height); mDividerAlpha = res.getFloat(R.dimen.notification_divider_alpha); - mNotificationHeaderMargin = res.getDimensionPixelOffset( - R.dimen.notification_children_container_margin_top); + mNotificationHeaderMargin = notificationsRedesignTemplates() + ? Notification.Builder.getContentMarginTop(getContext(), + R.dimen.notification_2025_children_container_margin_top) + : res.getDimensionPixelOffset(R.dimen.notification_children_container_margin_top); mNotificationTopPadding = res.getDimensionPixelOffset( R.dimen.notification_children_container_top_padding); mHeaderHeight = notificationsRedesignTemplates() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractor.kt index 2f7b24393ae0..2bfbf4826053 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractor.kt @@ -49,15 +49,18 @@ import kotlinx.coroutines.flow.stateIn /** * Interactor for determining whether to show a chip in the status bar for ongoing phone calls. * - * This class monitors call notifications and the visibility of call apps to determine the appropriate - * chip state. It emits: - * * - [OngoingCallModel.NoCall] when there is no call notification - * * - [OngoingCallModel.InCallWithVisibleApp] when there is a call notification but the call app is visible - * * - [OngoingCallModel.InCall] when there is a call notification and the call app is not visible - * */ + * This class monitors call notifications and the visibility of call apps to determine the + * appropriate chip state. It emits: + * * - [OngoingCallModel.NoCall] when there is no call notification + * * - [OngoingCallModel.InCallWithVisibleApp] when there is a call notification but the call app is + * visible + * * - [OngoingCallModel.InCall] when there is a call notification and the call app is not visible + */ @OptIn(ExperimentalCoroutinesApi::class) @SysUISingleton -class OngoingCallInteractor @Inject constructor( +class OngoingCallInteractor +@Inject +constructor( @Application private val scope: CoroutineScope, private val activityManagerRepository: ActivityManagerRepository, private val statusBarModeRepositoryStore: StatusBarModeRepositoryStore, @@ -68,44 +71,37 @@ class OngoingCallInteractor @Inject constructor( ) : CoreStartable { private val logger = Logger(logBuffer, TAG) - /** - * Tracks whether the call chip has been swiped away. - */ + /** Tracks whether the call chip has been swiped away. */ private val _isChipSwipedAway = MutableStateFlow(false) val isChipSwipedAway: StateFlow<Boolean> = _isChipSwipedAway.asStateFlow() - /** - * The current state of ongoing calls. - */ + /** The current state of ongoing calls. */ val ongoingCallState: StateFlow<OngoingCallModel> = activeNotificationsInteractor.ongoingCallNotification .flatMapLatest { notification -> - createOngoingCallStateFlow( - notification = notification - ) + createOngoingCallStateFlow(notification = notification) } .stateIn( scope = scope, started = SharingStarted.WhileSubscribed(), - initialValue = OngoingCallModel.NoCall + initialValue = OngoingCallModel.NoCall, ) @VisibleForTesting - val isStatusBarRequiredForOngoingCall = combine( - ongoingCallState, - isChipSwipedAway - ) { callState, chipSwipedAway -> - callState is OngoingCallModel.InCall && !chipSwipedAway - } + val isStatusBarRequiredForOngoingCall = + combine(ongoingCallState, isChipSwipedAway) { callState, chipSwipedAway -> + callState is OngoingCallModel.InCall && !chipSwipedAway + } @VisibleForTesting - val isGestureListeningEnabled = combine( - ongoingCallState, - statusBarModeRepositoryStore.defaultDisplay.isInFullscreenMode, - isChipSwipedAway - ) { callState, isFullscreen, chipSwipedAway -> - callState is OngoingCallModel.InCall && !chipSwipedAway && isFullscreen - } + val isGestureListeningEnabled = + combine( + ongoingCallState, + statusBarModeRepositoryStore.defaultDisplay.isInFullscreenMode, + isChipSwipedAway, + ) { callState, isFullscreen, chipSwipedAway -> + callState is OngoingCallModel.InCall && !chipSwipedAway && isFullscreen + } private fun createOngoingCallStateFlow( notification: ActiveNotificationModel? @@ -121,7 +117,7 @@ class OngoingCallInteractor @Inject constructor( creationUid = notification.uid, logger = logger, identifyingLogTag = TAG, - ) + ), ) { model, isVisible -> deriveOngoingCallState(model, isVisible) } @@ -130,22 +126,19 @@ class OngoingCallInteractor @Inject constructor( override fun start() { ongoingCallState .filterIsInstance<OngoingCallModel.NoCall>() - .onEach { - _isChipSwipedAway.value = false - }.launchIn(scope) + .onEach { _isChipSwipedAway.value = false } + .launchIn(scope) - isStatusBarRequiredForOngoingCall.onEach { statusBarRequired -> - setStatusBarRequiredForOngoingCall(statusBarRequired) - }.launchIn(scope) + isStatusBarRequiredForOngoingCall + .onEach { statusBarRequired -> setStatusBarRequiredForOngoingCall(statusBarRequired) } + .launchIn(scope) - isGestureListeningEnabled.onEach { isEnabled -> - updateGestureListening(isEnabled) - }.launchIn(scope) + isGestureListeningEnabled + .onEach { isEnabled -> updateGestureListening(isEnabled) } + .launchIn(scope) } - /** - * Callback that must run when the status bar is swiped while gesture listening is active. - */ + /** Callback that must run when the status bar is swiped while gesture listening is active. */ @VisibleForTesting fun onStatusBarSwiped() { logger.d("Status bar chip swiped away") @@ -154,13 +147,11 @@ class OngoingCallInteractor @Inject constructor( private fun deriveOngoingCallState( model: ActiveNotificationModel, - isVisible: Boolean + isVisible: Boolean, ): OngoingCallModel { return when { isVisible -> { - logger.d({ "Call app is visible: uid=$int1" }) { - int1 = model.uid - } + logger.d({ "Call app is visible: uid=$int1" }) { int1 = model.uid } OngoingCallModel.InCallWithVisibleApp } @@ -173,7 +164,7 @@ class OngoingCallInteractor @Inject constructor( startTimeMs = model.whenTime, notificationIconView = model.statusBarChipIconView, intent = model.contentIntent, - notificationKey = model.key + notificationKey = model.key, ) } } @@ -186,8 +177,9 @@ class OngoingCallInteractor @Inject constructor( statusBarModeRepositoryStore.defaultDisplay.setOngoingProcessRequiresStatusBarVisible( statusBarRequired ) - statusBarWindowControllerStore.defaultDisplay - .setOngoingProcessRequiresStatusBarVisible(statusBarRequired) + statusBarWindowControllerStore.defaultDisplay.setOngoingProcessRequiresStatusBarVisible( + statusBarRequired + ) } private fun updateGestureListening(isEnabled: Boolean) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java index a3dcc3b6f851..ece5a3facdf4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.policy; +import android.media.projection.StopReason; import com.android.systemui.Dumpable; import com.android.systemui.statusbar.policy.CastController.Callback; @@ -26,7 +27,7 @@ public interface CastController extends CallbackController<Callback>, Dumpable { void setCurrentUserId(int currentUserId); List<CastDevice> getCastDevices(); void startCasting(CastDevice device); - void stopCasting(CastDevice device); + void stopCasting(CastDevice device, @StopReason int stopReason); /** * @return whether we have a connected device. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java index 52f80fbf50fd..ab208506f203 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java @@ -185,13 +185,13 @@ public class CastControllerImpl implements CastController { } @Override - public void stopCasting(CastDevice device) { + public void stopCasting(CastDevice device, @StopReason int stopReason) { final boolean isProjection = device.getTag() instanceof MediaProjectionInfo; mLogger.logStopCasting(isProjection); if (isProjection) { final MediaProjectionInfo projection = (MediaProjectionInfo) device.getTag(); if (Objects.equals(mProjectionManager.getActiveProjectionInfo(), projection)) { - mProjectionManager.stopActiveProjection(StopReason.STOP_QS_TILE); + mProjectionManager.stopActiveProjection(stopReason); } else { mLogger.logStopCastingNoProjection(projection); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java index afff4858499a..a17f100904be 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java @@ -36,6 +36,7 @@ import android.app.Dialog; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; +import android.media.projection.StopReason; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -154,7 +155,7 @@ public class RecordingControllerTest extends SysuiTestCase { PendingIntent stopIntent = Mockito.mock(PendingIntent.class); mController.startCountdown(0, 0, startIntent, stopIntent); - mController.stopRecording(); + mController.stopRecording(StopReason.STOP_UNKNOWN); assertFalse(mController.isStarting()); assertFalse(mController.isRecording()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/DisplayTrackerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/DisplayTrackerImplTest.kt index 9fb752a11f56..4471c587efb2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/settings/DisplayTrackerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/settings/DisplayTrackerImplTest.kt @@ -17,7 +17,7 @@ package com.android.systemui.settings import android.hardware.display.DisplayManager -import android.hardware.display.DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS +import android.hardware.display.DisplayManager.PRIVATE_EVENT_TYPE_DISPLAY_BRIGHTNESS import android.hardware.display.DisplayManagerGlobal import android.os.Handler import android.testing.AndroidTestingRunner @@ -98,7 +98,7 @@ class DisplayTrackerImplTest : SysuiTestCase() { any(), any(), eq(0L), - eq(PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS), + eq(PRIVATE_EVENT_TYPE_DISPLAY_BRIGHTNESS), ) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java index 192d66c44aa0..af12d0119c58 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -167,7 +167,6 @@ import com.android.systemui.util.settings.SystemSettings; import com.android.systemui.util.time.SystemClock; import com.android.wm.shell.Flags; import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.WindowManagerShellWrapper; import com.android.wm.shell.bubbles.Bubble; import com.android.wm.shell.bubbles.BubbleData; import com.android.wm.shell.bubbles.BubbleDataRepository; @@ -184,6 +183,8 @@ import com.android.wm.shell.bubbles.StackEducationView; import com.android.wm.shell.bubbles.bar.BubbleBarLayerView; import com.android.wm.shell.bubbles.properties.BubbleProperties; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.DisplayImeController; +import com.android.wm.shell.common.DisplayInsetsController; import com.android.wm.shell.common.FloatingContentCoordinator; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; @@ -325,7 +326,9 @@ public class BubblesTest extends SysuiTestCase { @Mock private LauncherApps mLauncherApps; @Mock - private WindowManagerShellWrapper mWindowManagerShellWrapper; + private DisplayInsetsController mDisplayInsetsController; + @Mock + private DisplayImeController mDisplayImeController; @Mock private BubbleLogger mBubbleLogger; @Mock @@ -503,7 +506,7 @@ public class BubblesTest extends SysuiTestCase { mContext, mock(NotificationManager.class), mock(NotificationSettingsInteractor.class) - ); + ); interruptionDecisionProvider.start(); mShellTaskOrganizer = new ShellTaskOrganizer(mock(ShellInit.class), @@ -523,7 +526,8 @@ public class BubblesTest extends SysuiTestCase { mDataRepository, mStatusBarService, mWindowManager, - mWindowManagerShellWrapper, + mDisplayInsetsController, + mDisplayImeController, mUserManager, mLauncherApps, mBubbleLogger, @@ -1430,9 +1434,12 @@ public class BubblesTest extends SysuiTestCase { mPositioner, mBubbleController.getStackView(), new BubbleIconFactory(mContext, - mContext.getResources().getDimensionPixelSize(com.android.wm.shell.R.dimen.bubble_size), - mContext.getResources().getDimensionPixelSize(com.android.wm.shell.R.dimen.bubble_badge_size), - mContext.getResources().getColor(com.android.launcher3.icons.R.color.important_conversation), + mContext.getResources().getDimensionPixelSize( + com.android.wm.shell.R.dimen.bubble_size), + mContext.getResources().getDimensionPixelSize( + com.android.wm.shell.R.dimen.bubble_badge_size), + mContext.getResources().getColor( + com.android.launcher3.icons.R.color.important_conversation), mContext.getResources().getDimensionPixelSize( com.android.internal.R.dimen.importance_ring_stroke_width)), bubble, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/mediarouter/data/repository/FakeMediaRouterRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/mediarouter/data/repository/FakeMediaRouterRepository.kt index 8aa7a03710cb..d5637cbe36b5 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/mediarouter/data/repository/FakeMediaRouterRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/mediarouter/data/repository/FakeMediaRouterRepository.kt @@ -16,6 +16,7 @@ package com.android.systemui.mediarouter.data.repository +import android.media.projection.StopReason import com.android.systemui.statusbar.policy.CastDevice import kotlinx.coroutines.flow.MutableStateFlow @@ -25,7 +26,7 @@ class FakeMediaRouterRepository : MediaRouterRepository { var lastStoppedDevice: CastDevice? = null private set - override fun stopCasting(device: CastDevice) { + override fun stopCasting(device: CastDevice, @StopReason stopReason: Int) { lastStoppedDevice = device } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/screenrecord/data/repository/FakeScreenRecordRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/screenrecord/data/repository/FakeScreenRecordRepository.kt index 30b4763118a7..4c9e1740b3b5 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/screenrecord/data/repository/FakeScreenRecordRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/screenrecord/data/repository/FakeScreenRecordRepository.kt @@ -16,6 +16,7 @@ package com.android.systemui.screenrecord.data.repository +import android.media.projection.StopReason import com.android.systemui.screenrecord.data.model.ScreenRecordModel import kotlinx.coroutines.flow.MutableStateFlow @@ -25,7 +26,7 @@ class FakeScreenRecordRepository : ScreenRecordRepository { var stopRecordingInvoked = false - override suspend fun stopRecording() { + override suspend fun stopRecording(@StopReason stopReason: Int) { stopRecordingInvoked = true } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeCastController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeCastController.kt index 2df0c7a5386e..da6b2ae46d2d 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeCastController.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeCastController.kt @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.policy +import android.media.projection.StopReason import java.io.PrintWriter class FakeCastController : CastController { @@ -45,7 +46,7 @@ class FakeCastController : CastController { override fun startCasting(device: CastDevice?) {} - override fun stopCasting(device: CastDevice?) { + override fun stopCasting(device: CastDevice?, @StopReason stopReason: Int) { lastStoppedDevice = device } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/LeakCheckerCastController.java b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/LeakCheckerCastController.java index 2249bc0b667f..857dc8584be9 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/LeakCheckerCastController.java +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/LeakCheckerCastController.java @@ -16,6 +16,7 @@ package com.android.systemui.utils.leaks; +import android.media.projection.StopReason; import android.testing.LeakCheck; import com.android.systemui.statusbar.policy.CastController; @@ -51,7 +52,7 @@ public class LeakCheckerCastController extends BaseLeakChecker<Callback> impleme } @Override - public void stopCasting(CastDevice device) { + public void stopCasting(CastDevice device, @StopReason int stopReason) { } diff --git a/ravenwood/scripts/extract-last-soong-commands.py b/ravenwood/scripts/extract-last-soong-commands.py index c08d4aa799a5..0629b77029e0 100755 --- a/ravenwood/scripts/extract-last-soong-commands.py +++ b/ravenwood/scripts/extract-last-soong-commands.py @@ -32,7 +32,7 @@ re_command = re.compile(r''' ^\[.*?\] \s* (.*) ''', re.X) HEADER = r'''#!/bin/bash set -e # Stop on a failed command - +set -x # Print command line before executing cd "${ANDROID_BUILD_TOP:?}" ''' @@ -65,16 +65,8 @@ def main(args): command = m.groups()[0] count += 1 - out.write(f'### Command {count} ========\n') - - # Show the full command line before executing it. - out.write('#echo ' + shlex.quote(command) + '\n') - out.write('\n') - - # Execute the command. - out.write('#' + command + '\n') - - out.write('\n') + out.write(f'### Command {count} ========\n\n') + out.write('#' + command + '\n\n') continue diff --git a/ravenwood/texts/ravenwood-build.prop b/ravenwood/texts/ravenwood-build.prop index 93a18cffec50..7cc4454a6e56 100644 --- a/ravenwood/texts/ravenwood-build.prop +++ b/ravenwood/texts/ravenwood-build.prop @@ -42,3 +42,4 @@ ro.build.version.release=$$$ro.build.version.release ro.build.version.release_or_codename=$$$ro.build.version.release_or_codename ro.build.version.release_or_preview_display=$$$ro.build.version.release_or_preview_display ro.build.version.sdk=$$$ro.build.version.sdk +ro.build.version.sdk_full=$$$ro.build.version.sdk_full diff --git a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java index 669025f071c0..37276ddac75a 100644 --- a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java +++ b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java @@ -155,23 +155,8 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub { int callingPid = Binder.getCallingPid(); final SafeOneTimeExecuteAppFunctionCallback safeExecuteAppFunctionCallback = - new SafeOneTimeExecuteAppFunctionCallback(executeAppFunctionCallback, - new SafeOneTimeExecuteAppFunctionCallback.CompletionCallback() { - @Override - public void finalizeOnSuccess( - @NonNull ExecuteAppFunctionResponse result, - long executionStartTimeMillis) { - mLoggerWrapper.logAppFunctionSuccess(requestInternal, result, - callingUid, executionStartTimeMillis); - } - - @Override - public void finalizeOnError(@NonNull AppFunctionException error, - long executionStartTimeMillis) { - mLoggerWrapper.logAppFunctionError(requestInternal, - error.getErrorCode(), callingUid, executionStartTimeMillis); - } - }); + initializeSafeExecuteAppFunctionCallback( + requestInternal, executeAppFunctionCallback, callingUid); String validatedCallingPackage; try { @@ -576,6 +561,38 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub { } } + /** + * Returns a new {@link SafeOneTimeExecuteAppFunctionCallback} initialized with a {@link + * SafeOneTimeExecuteAppFunctionCallback.CompletionCallback} that logs the results. + */ + @VisibleForTesting + SafeOneTimeExecuteAppFunctionCallback initializeSafeExecuteAppFunctionCallback( + @NonNull ExecuteAppFunctionAidlRequest requestInternal, + @NonNull IExecuteAppFunctionCallback executeAppFunctionCallback, + int callingUid) { + return new SafeOneTimeExecuteAppFunctionCallback( + executeAppFunctionCallback, + new SafeOneTimeExecuteAppFunctionCallback.CompletionCallback() { + @Override + public void finalizeOnSuccess( + @NonNull ExecuteAppFunctionResponse result, + long executionStartTimeMillis) { + mLoggerWrapper.logAppFunctionSuccess( + requestInternal, result, callingUid, executionStartTimeMillis); + } + + @Override + public void finalizeOnError( + @NonNull AppFunctionException error, long executionStartTimeMillis) { + mLoggerWrapper.logAppFunctionError( + requestInternal, + error.getErrorCode(), + callingUid, + executionStartTimeMillis); + } + }); + } + private static class AppFunctionMetadataObserver implements ObserverCallback { @Nullable private final MetadataSyncAdapter mPerUserMetadataSyncAdapter; diff --git a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionsLoggerWrapper.java b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionsLoggerWrapper.java index 7ba1bbc536f6..666d8fe0044c 100644 --- a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionsLoggerWrapper.java +++ b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionsLoggerWrapper.java @@ -26,58 +26,99 @@ import android.content.pm.PackageManager; import android.os.SystemClock; import android.util.Slog; +import com.android.internal.annotations.VisibleForTesting; + import java.util.Objects; +import java.util.concurrent.Executor; /** Wraps AppFunctionsStatsLog. */ public class AppFunctionsLoggerWrapper { private static final String TAG = AppFunctionsLoggerWrapper.class.getSimpleName(); - private static final int SUCCESS_RESPONSE_CODE = -1; + @VisibleForTesting static final int SUCCESS_RESPONSE_CODE = -1; + + private final PackageManager mPackageManager; + private final Executor mLoggingExecutor; + private final AppFunctionsLoggerClock mLoggerClock; - private final Context mContext; + AppFunctionsLoggerWrapper(@NonNull Context context) { + this(context.getPackageManager(), LOGGING_THREAD_EXECUTOR, SystemClock::elapsedRealtime); + } - public AppFunctionsLoggerWrapper(@NonNull Context context) { - mContext = Objects.requireNonNull(context); + @VisibleForTesting + AppFunctionsLoggerWrapper( + @NonNull PackageManager packageManager, + @NonNull Executor executor, + AppFunctionsLoggerClock loggerClock) { + mLoggingExecutor = Objects.requireNonNull(executor); + mPackageManager = Objects.requireNonNull(packageManager); + mLoggerClock = loggerClock; } - void logAppFunctionSuccess(ExecuteAppFunctionAidlRequest request, - ExecuteAppFunctionResponse response, int callingUid, long executionStartTimeMillis) { - logAppFunctionsRequestReported(request, SUCCESS_RESPONSE_CODE, - response.getResponseDataSize(), callingUid, executionStartTimeMillis); + void logAppFunctionSuccess( + ExecuteAppFunctionAidlRequest request, + ExecuteAppFunctionResponse response, + int callingUid, + long executionStartTimeMillis) { + logAppFunctionsRequestReported( + request, + SUCCESS_RESPONSE_CODE, + response.getResponseDataSize(), + callingUid, + executionStartTimeMillis); } - void logAppFunctionError(ExecuteAppFunctionAidlRequest request, int errorCode, int callingUid, + void logAppFunctionError( + ExecuteAppFunctionAidlRequest request, + int errorCode, + int callingUid, long executionStartTimeMillis) { - logAppFunctionsRequestReported(request, errorCode, /* responseSizeBytes = */ 0, callingUid, + logAppFunctionsRequestReported( + request, + errorCode, + /* responseSizeBytes= */ 0, + callingUid, executionStartTimeMillis); } - private void logAppFunctionsRequestReported(ExecuteAppFunctionAidlRequest request, - int errorCode, int responseSizeBytes, int callingUid, long executionStartTimeMillis) { + private void logAppFunctionsRequestReported( + ExecuteAppFunctionAidlRequest request, + int errorCode, + int responseSizeBytes, + int callingUid, + long executionStartTimeMillis) { final long e2eRequestLatencyMillis = - SystemClock.elapsedRealtime() - request.getRequestTime(); + mLoggerClock.getCurrentTimeMillis() - request.getRequestTime(); final long requestOverheadMillis = - executionStartTimeMillis > 0 ? (executionStartTimeMillis - request.getRequestTime()) + executionStartTimeMillis > 0 + ? (executionStartTimeMillis - request.getRequestTime()) : e2eRequestLatencyMillis; - LOGGING_THREAD_EXECUTOR.execute(() -> AppFunctionsStatsLog.write( - AppFunctionsStatsLog.APP_FUNCTIONS_REQUEST_REPORTED, - /* callerPackageUid= */ callingUid, - /* targetPackageUid= */ - getPackageUid(request.getClientRequest().getTargetPackageName()), - /* errorCode= */ errorCode, - /* requestSizeBytes= */ request.getClientRequest().getRequestDataSize(), - /* responseSizeBytes= */ responseSizeBytes, - /* requestDurationMs= */ e2eRequestLatencyMillis, - /* requestOverheadMs= */ requestOverheadMillis) - ); + mLoggingExecutor.execute( + () -> + AppFunctionsStatsLog.write( + AppFunctionsStatsLog.APP_FUNCTIONS_REQUEST_REPORTED, + /* callerPackageUid= */ callingUid, + /* targetPackageUid= */ getPackageUid( + request.getClientRequest().getTargetPackageName()), + /* errorCode= */ errorCode, + /* requestSizeBytes= */ request.getClientRequest() + .getRequestDataSize(), + /* responseSizeBytes= */ responseSizeBytes, + /* requestDurationMs= */ e2eRequestLatencyMillis, + /* requestOverheadMs= */ requestOverheadMillis)); } private int getPackageUid(String packageName) { try { - return mContext.getPackageManager().getPackageUid(packageName, 0); + return mPackageManager.getPackageUid(packageName, 0); } catch (PackageManager.NameNotFoundException e) { Slog.e(TAG, "Package uid not found for " + packageName); } return 0; } + + /** Wraps a custom clock for easier testing. */ + interface AppFunctionsLoggerClock { + long getCurrentTimeMillis(); + } } diff --git a/services/appfunctions/java/com/android/server/appfunctions/RunAppFunctionServiceCallback.java b/services/appfunctions/java/com/android/server/appfunctions/RunAppFunctionServiceCallback.java index a5ae7e310022..4cba8ecb2092 100644 --- a/services/appfunctions/java/com/android/server/appfunctions/RunAppFunctionServiceCallback.java +++ b/services/appfunctions/java/com/android/server/appfunctions/RunAppFunctionServiceCallback.java @@ -23,6 +23,7 @@ import android.app.appfunctions.IAppFunctionService; import android.app.appfunctions.ICancellationCallback; import android.app.appfunctions.IExecuteAppFunctionCallback; import android.app.appfunctions.SafeOneTimeExecuteAppFunctionCallback; +import android.os.SystemClock; import android.util.Slog; import com.android.server.appfunctions.RemoteServiceCaller.RunServiceCallCallback; @@ -52,7 +53,8 @@ public class RunAppFunctionServiceCallback implements RunServiceCallCallback<IAp @NonNull IAppFunctionService service, @NonNull ServiceUsageCompleteListener serviceUsageCompleteListener) { try { - mSafeExecuteAppFunctionCallback.setExecutionStartTimeMillis(); + mSafeExecuteAppFunctionCallback.setExecutionStartTimeAfterBindMillis( + SystemClock.elapsedRealtime()); service.executeAppFunction( mRequestInternal.getClientRequest(), mRequestInternal.getCallingPackage(), @@ -73,8 +75,7 @@ public class RunAppFunctionServiceCallback implements RunServiceCallCallback<IAp } catch (Exception e) { mSafeExecuteAppFunctionCallback.onError( new AppFunctionException( - AppFunctionException.ERROR_APP_UNKNOWN_ERROR, - e.getMessage())); + AppFunctionException.ERROR_APP_UNKNOWN_ERROR, e.getMessage())); serviceUsageCompleteListener.onCompleted(); } } @@ -83,7 +84,8 @@ public class RunAppFunctionServiceCallback implements RunServiceCallCallback<IAp public void onFailedToConnect() { Slog.e(TAG, "Failed to connect to service"); mSafeExecuteAppFunctionCallback.onError( - new AppFunctionException(AppFunctionException.ERROR_APP_UNKNOWN_ERROR, + new AppFunctionException( + AppFunctionException.ERROR_APP_UNKNOWN_ERROR, "Failed to connect to AppFunctionService")); } diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java index a1d621d8dd1f..6bf60bf1ddf1 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java @@ -301,14 +301,19 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub // if the secure window is shown on a non-secure virtual display. DisplayManager displayManager = mContext.getSystemService(DisplayManager.class); Display display = displayManager.getDisplay(displayId); - if ((display.getFlags() & Display.FLAG_SECURE) == 0) { - showToastWhereUidIsRunning(activityInfo.applicationInfo.uid, - com.android.internal.R.string.vdm_secure_window, - Toast.LENGTH_LONG, mContext.getMainLooper()); - - Counter.logIncrementWithUid( - "virtual_devices.value_secure_window_blocked_count", - mAttributionSource.getUid()); + if (display != null) { + if ((display.getFlags() & Display.FLAG_SECURE) == 0) { + showToastWhereUidIsRunning(activityInfo.applicationInfo.uid, + com.android.internal.R.string.vdm_secure_window, + Toast.LENGTH_LONG, mContext.getMainLooper()); + + Counter.logIncrementWithUid( + "virtual_devices.value_secure_window_blocked_count", + mAttributionSource.getUid()); + } + } else { + Slog.e(TAG, "Calling onSecureWindowShown on a non existent/connected display: " + + displayId); } } diff --git a/services/core/java/com/android/server/display/BrightnessRangeController.java b/services/core/java/com/android/server/display/BrightnessRangeController.java index 4fd92751d498..0f0c9009b4d8 100644 --- a/services/core/java/com/android/server/display/BrightnessRangeController.java +++ b/services/core/java/com/android/server/display/BrightnessRangeController.java @@ -59,7 +59,7 @@ class BrightnessRangeController { mModeChangeCallback = modeChangeCallback; mHdrClamper = hdrClamper; mNormalBrightnessModeController = normalBrightnessModeController; - mUseHdrClamper = flags.isHdrClamperEnabled() && !flags.useNewHdrBrightnessModifier(); + mUseHdrClamper = !flags.useNewHdrBrightnessModifier(); mNormalBrightnessModeController.resetNbmData( displayDeviceConfig.getLuxThrottlingData()); if (flags.useNewHdrBrightnessModifier()) { diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java index 85b6bbb40b91..addfbf1833b9 100644 --- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java +++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java @@ -46,10 +46,6 @@ public class DisplayManagerFlags { Flags.FLAG_ENABLE_CONNECTED_DISPLAY_MANAGEMENT, Flags::enableConnectedDisplayManagement); - private final FlagState mHdrClamperFlagState = new FlagState( - Flags.FLAG_ENABLE_HDR_CLAMPER, - Flags::enableHdrClamper); - private final FlagState mAdaptiveToneImprovements1 = new FlagState( Flags.FLAG_ENABLE_ADAPTIVE_TONE_IMPROVEMENTS_1, Flags::enableAdaptiveToneImprovements1); @@ -278,11 +274,6 @@ public class DisplayManagerFlags { return mConnectedDisplayManagementFlagState.isEnabled(); } - /** Returns whether hdr clamper is enabled on not. */ - public boolean isHdrClamperEnabled() { - return mHdrClamperFlagState.isEnabled(); - } - /** Returns whether power throttling clamper is enabled on not. */ public boolean isPowerThrottlingClamperEnabled() { return mPowerThrottlingClamperFlagState.isEnabled(); @@ -585,7 +576,6 @@ public class DisplayManagerFlags { pw.println(" " + mDisplayOffloadFlagState); pw.println(" " + mExternalDisplayLimitModeState); pw.println(" " + mDisplayTopology); - pw.println(" " + mHdrClamperFlagState); pw.println(" " + mPowerThrottlingClamperFlagState); pw.println(" " + mEvenDimmerFlagState); pw.println(" " + mSmallAreaDetectionFlagState); diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig index 5f974104f86b..eccbbb14c4ea 100644 --- a/services/core/java/com/android/server/display/feature/display_flags.aconfig +++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig @@ -37,14 +37,6 @@ flag { } flag { - name: "enable_hdr_clamper" - namespace: "display_manager" - description: "Feature flag for HDR Clamper" - bug: "295100043" - is_fixed_read_only: true -} - -flag { name: "enable_power_throttling_clamper" namespace: "display_manager" description: "Feature flag for Power Throttling Clamper" diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java index 02e2882442bf..1dd4a9b93277 100644 --- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java @@ -2080,10 +2080,13 @@ public class DisplayModeDirector { restartObserver(); mDeviceConfigDisplaySettings.startListening(); + registerDisplayListener(); + } + private void registerDisplayListener() { mInjector.registerDisplayListener(this, mHandler, - DisplayManager.EVENT_FLAG_DISPLAY_CHANGED, - DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS); + DisplayManager.EVENT_TYPE_DISPLAY_CHANGED, + DisplayManager.PRIVATE_EVENT_TYPE_DISPLAY_BRIGHTNESS); } private void setLoggingEnabled(boolean loggingEnabled) { @@ -2883,8 +2886,8 @@ public class DisplayModeDirector { } mDisplayManagerInternal = mInjector.getDisplayManagerInternal(); mInjector.registerDisplayListener(this, mHandler, - DisplayManager.EVENT_FLAG_DISPLAY_REMOVED, - DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS); + DisplayManager.EVENT_TYPE_DISPLAY_REMOVED, + DisplayManager.PRIVATE_EVENT_TYPE_DISPLAY_BRIGHTNESS); } /** diff --git a/services/core/java/com/android/server/display/mode/ProximitySensorObserver.java b/services/core/java/com/android/server/display/mode/ProximitySensorObserver.java index 11418c147caa..0f78f0df006b 100644 --- a/services/core/java/com/android/server/display/mode/ProximitySensorObserver.java +++ b/services/core/java/com/android/server/display/mode/ProximitySensorObserver.java @@ -70,10 +70,14 @@ class ProximitySensorObserver implements mDozeStateByDisplay.put(d.getDisplayId(), mInjector.isDozeState(d)); } } + registerDisplayListener(); + } + + private void registerDisplayListener() { mInjector.registerDisplayListener(this, BackgroundThread.getHandler(), - DisplayManager.EVENT_FLAG_DISPLAY_ADDED - | DisplayManager.EVENT_FLAG_DISPLAY_CHANGED - | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED); + DisplayManager.EVENT_TYPE_DISPLAY_ADDED + | DisplayManager.EVENT_TYPE_DISPLAY_CHANGED + | DisplayManager.EVENT_TYPE_DISPLAY_REMOVED); } @GuardedBy("mSensorObserverLock") diff --git a/services/core/java/com/android/server/display/mode/SkinThermalStatusObserver.java b/services/core/java/com/android/server/display/mode/SkinThermalStatusObserver.java index 625b6bbc0e83..4ec9e839a02f 100644 --- a/services/core/java/com/android/server/display/mode/SkinThermalStatusObserver.java +++ b/services/core/java/com/android/server/display/mode/SkinThermalStatusObserver.java @@ -83,12 +83,15 @@ final class SkinThermalStatusObserver extends IThermalEventListener.Stub impleme if (!mInjector.registerThermalServiceListener(this)) { return; } + registerDisplayListener(); + populateInitialDisplayInfo(); + } + private void registerDisplayListener() { mInjector.registerDisplayListener(this, mHandler, - DisplayManager.EVENT_FLAG_DISPLAY_ADDED | DisplayManager.EVENT_FLAG_DISPLAY_CHANGED - | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED); - - populateInitialDisplayInfo(); + DisplayManager.EVENT_TYPE_DISPLAY_ADDED + | DisplayManager.EVENT_TYPE_DISPLAY_CHANGED + | DisplayManager.EVENT_TYPE_DISPLAY_REMOVED); } void setLoggingEnabled(boolean enabled) { diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java index a8d5696e8c77..c384b5434bce 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java @@ -1345,7 +1345,10 @@ abstract class HdmiCecLocalDevice extends HdmiLocalDevice { iter.remove(); } if (mPendingActionClearedCallback != null) { - mPendingActionClearedCallback.onCleared(this); + PendingActionClearedCallback callback = mPendingActionClearedCallback; + // To prevent from calling the callback again during handling the callback itself. + mPendingActionClearedCallback = null; + callback.onCleared(this); } } diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java b/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java index ff40eec0d6c8..7b59d6f1301e 100644 --- a/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java +++ b/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java @@ -482,10 +482,7 @@ import java.util.List; /* package */ static Message createHalMessage(HubMessage message) { Message outMessage = new Message(); - outMessage.flags = - message.getDeliveryParams().isResponseRequired() - ? Message.FLAG_REQUIRES_DELIVERY_STATUS - : 0; + outMessage.flags = message.isResponseRequired() ? Message.FLAG_REQUIRES_DELIVERY_STATUS : 0; outMessage.permissions = new String[0]; outMessage.sequenceNumber = message.getMessageSequenceNumber(); outMessage.type = message.getMessageType(); @@ -502,8 +499,9 @@ import java.util.List; /* package */ static HubMessage createHubMessage(Message message) { boolean isReliable = (message.flags & Message.FLAG_REQUIRES_DELIVERY_STATUS) != 0; - return new HubMessage( - message.type, message.content, new HubMessage.DeliveryParams(isReliable)); + return new HubMessage.Builder(message.type, message.content) + .setResponseRequired(isReliable) + .build(); } /** diff --git a/services/core/java/com/android/server/power/WakefulnessSessionObserver.java b/services/core/java/com/android/server/power/WakefulnessSessionObserver.java index 64f0693f14c4..d377c23c9b09 100644 --- a/services/core/java/com/android/server/power/WakefulnessSessionObserver.java +++ b/services/core/java/com/android/server/power/WakefulnessSessionObserver.java @@ -205,6 +205,13 @@ public class WakefulnessSessionObserver { UserHandle.USER_ALL); mPhysicalDisplayPortIdForDefaultDisplay = getPhysicalDisplayPortId(DEFAULT_DISPLAY); + registerDisplayListener(); + mPowerGroups.append( + Display.DEFAULT_DISPLAY_GROUP, + new WakefulnessSessionPowerGroup(Display.DEFAULT_DISPLAY_GROUP)); + } + + private void registerDisplayListener() { DisplayManager displayManager = mContext.getSystemService(DisplayManager.class); if (displayManager != null) { displayManager.registerDisplayListener( @@ -226,12 +233,8 @@ public class WakefulnessSessionObserver { } }, mHandler, - DisplayManager.EVENT_FLAG_DISPLAY_CHANGED); + DisplayManager.EVENT_TYPE_DISPLAY_CHANGED); } - - mPowerGroups.append( - Display.DEFAULT_DISPLAY_GROUP, - new WakefulnessSessionPowerGroup(Display.DEFAULT_DISPLAY_GROUP)); } /** diff --git a/services/core/java/com/android/server/power/hint/HintManagerService.java b/services/core/java/com/android/server/power/hint/HintManagerService.java index a0bc77e939d1..c4e4c422688d 100644 --- a/services/core/java/com/android/server/power/hint/HintManagerService.java +++ b/services/core/java/com/android/server/power/hint/HintManagerService.java @@ -42,6 +42,7 @@ import android.hardware.power.GpuHeadroomParams; import android.hardware.power.GpuHeadroomResult; import android.hardware.power.IPower; import android.hardware.power.SessionConfig; +import android.hardware.power.SessionMode; import android.hardware.power.SessionTag; import android.hardware.power.SupportInfo; import android.hardware.power.WorkDuration; @@ -58,6 +59,7 @@ import android.os.PerformanceHintManager; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.ServiceSpecificException; import android.os.SessionCreationConfig; import android.os.SystemProperties; import android.os.UserHandle; @@ -80,7 +82,6 @@ import com.android.server.FgThread; import com.android.server.LocalServices; import com.android.server.ServiceThread; import com.android.server.SystemService; -import com.android.server.power.hint.HintManagerService.AppHintSession.SessionModes; import com.android.server.utils.Slogf; import java.io.BufferedReader; @@ -409,6 +410,29 @@ public final class HintManagerService extends SystemService { mEnforceCpuHeadroomUserModeCpuTimeCheck = true; } + private boolean tooManyPipelineThreads(int uid) { + synchronized (mThreadsUsageObject) { + ArraySet<ThreadUsageTracker> threadsSet = mThreadsUsageMap.get(uid); + int graphicsPipelineThreadCount = 0; + if (threadsSet != null) { + // We count the graphics pipeline threads that are + // *not* in this session, since those in this session + // will be replaced. Then if the count plus the new tids + // is over max available graphics pipeline threads we raise + // an exception. + for (ThreadUsageTracker t : threadsSet) { + if (t.isGraphicsPipeline()) { + graphicsPipelineThreadCount++; + } + } + if (graphicsPipelineThreadCount > MAX_GRAPHICS_PIPELINE_THREADS_COUNT) { + return true; + } + } + return false; + } + } + private ServiceThread createCleanUpThread() { final ServiceThread handlerThread = new ServiceThread(TAG, Process.THREAD_PRIORITY_LOWEST, true /*allowIo*/); @@ -1307,9 +1331,9 @@ public final class HintManagerService extends SystemService { @VisibleForTesting final class BinderService extends IHintManager.Stub { @Override - public IHintSession createHintSessionWithConfig(@NonNull IBinder token, - @SessionTag int tag, SessionCreationConfig creationConfig, - SessionConfig config) { + public IHintManager.SessionCreationReturn createHintSessionWithConfig( + @NonNull IBinder token, @SessionTag int tag, + SessionCreationConfig creationConfig, SessionConfig config) { if (!isHintSessionSupported()) { throw new UnsupportedOperationException("PowerHintSessions are not supported!"); } @@ -1327,8 +1351,24 @@ public final class HintManagerService extends SystemService { final long identity = Binder.clearCallingIdentity(); final long durationNanos = creationConfig.targetWorkDurationNanos; - Preconditions.checkArgument(checkGraphicsPipelineValid(creationConfig, callingUid), - "not enough of available graphics pipeline thread."); + boolean isGraphicsPipeline = false; + boolean isAutoTimed = false; + if (creationConfig.modesToEnable != null) { + for (int mode : creationConfig.modesToEnable) { + if (mode == SessionMode.GRAPHICS_PIPELINE) { + isGraphicsPipeline = true; + } + if (mode == SessionMode.AUTO_CPU || mode == SessionMode.AUTO_GPU) { + isAutoTimed = true; + } + } + } + + if (isAutoTimed) { + Preconditions.checkArgument(isGraphicsPipeline, + "graphics pipeline mode not enabled for an automatically timed session"); + } + try { final IntArray nonIsolated = powerhintThreadCleanup() ? new IntArray(tids.length) : null; @@ -1446,12 +1486,8 @@ public final class HintManagerService extends SystemService { } if (hs != null) { - boolean isGraphicsPipeline = false; if (creationConfig.modesToEnable != null) { for (int sessionMode : creationConfig.modesToEnable) { - if (sessionMode == SessionModes.GRAPHICS_PIPELINE.ordinal()) { - isGraphicsPipeline = true; - } hs.setMode(sessionMode, true); } } @@ -1470,7 +1506,10 @@ public final class HintManagerService extends SystemService { } } - return hs; + IHintManager.SessionCreationReturn out = new IHintManager.SessionCreationReturn(); + out.pipelineThreadLimitExceeded = tooManyPipelineThreads(callingUid); + out.session = hs; + return out; } finally { Binder.restoreCallingIdentity(identity); } @@ -1852,45 +1891,6 @@ public final class HintManagerService extends SystemService { throw new IllegalStateException("Can't find cpu line in " + filePath); } - private boolean checkGraphicsPipelineValid(SessionCreationConfig creationConfig, int uid) { - if (creationConfig.modesToEnable == null) { - return true; - } - boolean setGraphicsPipeline = false; - for (int modeToEnable : creationConfig.modesToEnable) { - if (modeToEnable == SessionModes.GRAPHICS_PIPELINE.ordinal()) { - setGraphicsPipeline = true; - } - } - if (!setGraphicsPipeline) { - return true; - } - - synchronized (mThreadsUsageObject) { - // count used graphics pipeline threads for the calling UID - // consider the case that new tids are overlapping with in session tids - ArraySet<ThreadUsageTracker> threadsSet = mThreadsUsageMap.get(uid); - if (threadsSet == null) { - return true; - } - - final int newThreadCount = creationConfig.tids.length; - int graphicsPipelineThreadCount = 0; - for (ThreadUsageTracker t : threadsSet) { - // count graphics pipeline threads in use - // and exclude overlapping ones - if (t.isGraphicsPipeline()) { - graphicsPipelineThreadCount++; - if (contains(creationConfig.tids, t.getTid())) { - graphicsPipelineThreadCount--; - } - } - } - return graphicsPipelineThreadCount + newThreadCount - <= MAX_GRAPHICS_PIPELINE_THREADS_COUNT; - } - } - private void logPerformanceHintSessionAtom(int uid, long sessionId, long targetDuration, int[] tids, @SessionTag int sessionTag) { FrameworkStatsLog.write(FrameworkStatsLog.PERFORMANCE_HINT_SESSION_REPORTED, uid, @@ -1928,11 +1928,6 @@ public final class HintManagerService extends SystemService { protected Integer mSessionId; protected boolean mTrackedBySF; - enum SessionModes { - POWER_EFFICIENCY, - GRAPHICS_PIPELINE, - }; - protected AppHintSession( int uid, int pid, int sessionTag, int[] threadIds, IBinder token, long halSessionPtr, long durationNanos, Integer sessionId) { @@ -1985,8 +1980,8 @@ public final class HintManagerService extends SystemService { if (!isHintAllowed()) { return; } - Preconditions.checkArgument(targetDurationNanos > 0, "Expected" - + " the target duration to be greater than 0."); + Preconditions.checkArgument(targetDurationNanos >= 0, "Expected" + + " the target duration to be greater than or equal to 0."); mNativeWrapper.halUpdateTargetWorkDuration(mHalSessionPtr, targetDurationNanos); mTargetDurationNanos = targetDurationNanos; } @@ -2149,6 +2144,11 @@ public final class HintManagerService extends SystemService { public void setThreads(@NonNull int[] tids) { setThreadsInternal(tids, true); + if (tooManyPipelineThreads(Binder.getCallingUid())) { + // This is technically a success but we are going to throw a fit anyway + throw new ServiceSpecificException(5, + "Not enough available graphics pipeline threads."); + } } private void setThreadsInternal(int[] tids, boolean checkTid) { @@ -2156,32 +2156,7 @@ public final class HintManagerService extends SystemService { throw new IllegalArgumentException("Thread id list can't be empty."); } - final int callingUid = Binder.getCallingUid(); - if (mGraphicsPipeline) { - synchronized (mThreadsUsageObject) { - // replace original tids with new tids - ArraySet<ThreadUsageTracker> threadsSet = mThreadsUsageMap.get(callingUid); - int graphicsPipelineThreadCount = 0; - if (threadsSet != null) { - // We count the graphics pipeline threads that are - // *not* in this session, since those in this session - // will be replaced. Then if the count plus the new tids - // is over max available graphics pipeline threads we raise - // an exception. - for (ThreadUsageTracker t : threadsSet) { - if (t.isGraphicsPipeline() && !contains(mThreadIds, t.getTid())) { - graphicsPipelineThreadCount++; - } - } - if (graphicsPipelineThreadCount + tids.length - > MAX_GRAPHICS_PIPELINE_THREADS_COUNT) { - throw new IllegalArgumentException( - "Not enough available graphics pipeline threads."); - } - } - } - } synchronized (this) { if (mHalSessionPtr == 0) { @@ -2315,15 +2290,15 @@ public final class HintManagerService extends SystemService { } Preconditions.checkArgument(mode >= 0, "the mode Id value should be" + " greater than zero."); - if (mode == SessionModes.POWER_EFFICIENCY.ordinal()) { + if (mode == SessionMode.POWER_EFFICIENCY) { mPowerEfficient = enabled; - } else if (mode == SessionModes.GRAPHICS_PIPELINE.ordinal()) { + } else if (mode == SessionMode.GRAPHICS_PIPELINE) { mGraphicsPipeline = enabled; } mNativeWrapper.halSetMode(mHalSessionPtr, mode, enabled); } if (enabled) { - if (mode == SessionModes.POWER_EFFICIENCY.ordinal()) { + if (mode == SessionMode.POWER_EFFICIENCY) { if (!mHasBeenPowerEfficient) { mHasBeenPowerEfficient = true; synchronized (mSessionSnapshotMapLock) { @@ -2342,7 +2317,7 @@ public final class HintManagerService extends SystemService { sessionSnapshot.logPowerEfficientSession(); } } - } else if (mode == SessionModes.GRAPHICS_PIPELINE.ordinal()) { + } else if (mode == SessionMode.GRAPHICS_PIPELINE) { if (!mHasBeenGraphicsPipeline) { mHasBeenGraphicsPipeline = true; synchronized (mSessionSnapshotMapLock) { diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index 0aff1de72cb1..bf57f56df7c2 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -77,6 +77,7 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLAS import static com.android.server.wm.ActivityTaskManagerService.ANIMATE; import static com.android.server.wm.ActivityTaskManagerService.H.FIRST_SUPERVISOR_TASK_MSG; import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE; +import static com.android.server.wm.ActivityTaskManagerService.isPip2ExperimentEnabled; import static com.android.server.wm.ClientLifecycleManager.shouldDispatchLaunchActivityItemIndependently; import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_ALLOWLISTED; import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_LAUNCHABLE; @@ -2525,7 +2526,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { void scheduleUpdatePictureInPictureModeIfNeeded(Task task, Rect targetRootTaskBounds) { task.forAllActivities(r -> { if (!r.attachedToProcess()) return; - mPipModeChangedActivities.add(r); + if (!isPip2ExperimentEnabled()) mPipModeChangedActivities.add(r); // If we are scheduling pip change, then remove this activity from multi-window // change list as the processing of pip change will make sure multi-window changed // message is processed in the right order relative to pip changed. @@ -2534,7 +2535,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { mPipModeChangedTargetRootTaskBounds = targetRootTaskBounds; - if (!mHandler.hasMessages(REPORT_PIP_MODE_CHANGED_MSG)) { + if (!isPip2ExperimentEnabled() && !mHandler.hasMessages(REPORT_PIP_MODE_CHANGED_MSG)) { mHandler.sendEmptyMessage(REPORT_PIP_MODE_CHANGED_MSG); } } diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java index 89d756ca5aaf..f9a06e2dd04b 100644 --- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java +++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java @@ -616,7 +616,6 @@ public class BackgroundActivityStartController { } static class BalVerdict { - static final BalVerdict BLOCK = new BalVerdict(BAL_BLOCK, false, "Blocked"); static final BalVerdict ALLOW_BY_DEFAULT = new BalVerdict(BAL_ALLOW_DEFAULT, false, "Default"); @@ -640,6 +639,9 @@ public class BackgroundActivityStartController { } public BalVerdict withProcessInfo(String msg, WindowProcessController process) { + if (this == BLOCK || this == ALLOW_BY_DEFAULT || this == ALLOW_PRIVILEGED) { + return this; + } mProcessInfo = msg + " (uid=" + process.mUid + ",pid=" + process.getPid() + ")"; return this; } @@ -653,6 +655,10 @@ public class BackgroundActivityStartController { } void setOnlyCreatorAllows(boolean onlyCreatorAllows) { + if (this == BLOCK) { + // do not modify BLOCK constant + return; + } mOnlyCreatorAllows = onlyCreatorAllows; } @@ -662,6 +668,10 @@ public class BackgroundActivityStartController { @VisibleForTesting BalVerdict setBasedOnRealCaller() { + if (this == BLOCK) { + // do not modify BLOCK constant + return this; + } mBasedOnRealCaller = true; return this; } @@ -669,22 +679,29 @@ public class BackgroundActivityStartController { public String toString() { StringBuilder builder = new StringBuilder(); builder.append(balCodeToString(mCode)); - if (DEBUG_ACTIVITY_STARTS) { - builder.append(" ("); - if (mBackground) { - builder.append("Background "); - } - builder.append("Activity start "); - if (mCode == BAL_BLOCK) { - builder.append("denied"); - } else { - builder.append("allowed: ").append(mMessage); + if (this != BLOCK) { + if (mOnlyCreatorAllows) { + builder.append(" [onlyCaller]"); + } else if (mBasedOnRealCaller) { + builder.append(" [realCaller]"); } - if (mProcessInfo != null) { - builder.append(" "); - builder.append(mProcessInfo); + if (DEBUG_ACTIVITY_STARTS) { + builder.append(" ("); + if (mBackground) { + builder.append("Background "); + } + builder.append("Activity start "); + if (mCode == BAL_BLOCK) { + builder.append("denied"); + } else { + builder.append("allowed: ").append(mMessage); + } + if (mProcessInfo != null) { + builder.append(" "); + builder.append(mProcessInfo); + } + builder.append(")"); } - builder.append(")"); } return builder.toString(); } @@ -785,9 +802,8 @@ public class BackgroundActivityStartController { .setBasedOnRealCaller(); state.setResultForRealCaller(resultForRealCaller); - if (state.isPendingIntent()) { - resultForCaller.setOnlyCreatorAllows( - resultForCaller.allows() && resultForRealCaller.blocks()); + if (state.isPendingIntent() && resultForCaller.allows() && resultForRealCaller.blocks()) { + resultForCaller.setOnlyCreatorAllows(true); } // Handle cases with explicit opt-in diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index fb197c566b7d..e45ada9438ae 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -80,6 +80,7 @@ import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_WINDOW_ORG import static com.android.server.wm.ActivityRecord.State.PAUSING; import static com.android.server.wm.ActivityRecord.State.RESUMED; import static com.android.server.wm.ActivityTaskManagerService.enforceTaskPermission; +import static com.android.server.wm.ActivityTaskManagerService.isPip2ExperimentEnabled; import static com.android.server.wm.ActivityTaskSupervisor.REMOVE_FROM_RECENTS; import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_PINNED_TASK; import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG; @@ -716,6 +717,8 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } if (forceHiddenForPip) { wc.asTask().setForceHidden(FLAG_FORCE_HIDDEN_FOR_PINNED_TASK, true /* set */); + } + if (forceHiddenForPip && !isPip2ExperimentEnabled()) { // When removing pip, make sure that onStop is sent to the app ahead of // onPictureInPictureModeChanged. // See also PinnedStackTests#testStopBeforeMultiWindowCallbacksOnDismiss diff --git a/services/tests/appfunctions/Android.bp b/services/tests/appfunctions/Android.bp index 836f90b992d6..e48abc24ecf3 100644 --- a/services/tests/appfunctions/Android.bp +++ b/services/tests/appfunctions/Android.bp @@ -33,18 +33,20 @@ android_test { ], static_libs: [ + "androidx.core_core-ktx", "androidx.test.core", - "androidx.test.runner", "androidx.test.ext.truth", - "androidx.core_core-ktx", + "androidx.test.rules", + "androidx.test.runner", + "frameworks-base-testutils", "kotlin-test", "kotlinx_coroutines_test", + "mockito-kotlin2", + "mockito-target-extended-minus-junit4", "platform-test-annotations", "services.appfunctions", "servicestests-core-utils", "truth", - "frameworks-base-testutils", - "androidx.test.rules", ], libs: [ diff --git a/services/tests/appfunctions/src/com/android/server/appfunctions/AppFunctionsLoggingTest.kt b/services/tests/appfunctions/src/com/android/server/appfunctions/AppFunctionsLoggingTest.kt new file mode 100644 index 000000000000..896d2a21d0ac --- /dev/null +++ b/services/tests/appfunctions/src/com/android/server/appfunctions/AppFunctionsLoggingTest.kt @@ -0,0 +1,139 @@ +/* + * Copyright (C) 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 com.android.server.appfunctions + +import android.app.appfunctions.AppFunctionException +import android.app.appfunctions.ExecuteAppFunctionAidlRequest +import android.app.appfunctions.ExecuteAppFunctionRequest +import android.app.appfunctions.ExecuteAppFunctionResponse +import android.app.appfunctions.IAppFunctionService +import android.app.appfunctions.IExecuteAppFunctionCallback +import android.app.appfunctions.SafeOneTimeExecuteAppFunctionCallback +import android.app.appsearch.GenericDocument +import android.content.Context +import android.content.pm.PackageManager +import android.os.UserHandle +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.dx.mockito.inline.extended.ExtendedMockito +import com.android.modules.utils.testing.ExtendedMockitoRule +import com.google.common.util.concurrent.MoreExecutors +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.kotlin.any +import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.whenever + + +/** + * Tests that AppFunctionsStatsLog logs AppFunctionsRequestReported with the expected values. + */ +@RunWith(AndroidJUnit4::class) +class AppFunctionsLoggingTest { + @get:Rule + val mExtendedMockitoRule: ExtendedMockitoRule = + ExtendedMockitoRule.Builder(this) + .mockStatic(AppFunctionsStatsLog::class.java) + .build() + private val mContext: Context get() = ApplicationProvider.getApplicationContext() + private val mMockPackageManager = mock<PackageManager>() + private val mAppFunctionsLoggerWrapper = + AppFunctionsLoggerWrapper( + mMockPackageManager, + MoreExecutors.directExecutor(), + { TEST_CURRENT_TIME_MILLIS }) + private lateinit var mSafeCallback: SafeOneTimeExecuteAppFunctionCallback + + private val mServiceImpl = + AppFunctionManagerServiceImpl( + mContext, + mock<RemoteServiceCaller<IAppFunctionService>>(), + mock<CallerValidator>(), + mock<ServiceHelper>(), + ServiceConfigImpl(), + mAppFunctionsLoggerWrapper) + + private val mRequestInternal = ExecuteAppFunctionAidlRequest( + ExecuteAppFunctionRequest.Builder(TEST_TARGET_PACKAGE, TEST_FUNCTION_ID).build(), + UserHandle.CURRENT, TEST_CALLING_PKG, TEST_INITIAL_REQUEST_TIME_MILLIS + ) + + @Before + fun setup() { + whenever(mMockPackageManager.getPackageUid(eq(TEST_TARGET_PACKAGE), any<Int>())).thenReturn(TEST_TARGET_UID) + mSafeCallback = mServiceImpl.initializeSafeExecuteAppFunctionCallback(mRequestInternal, mock<IExecuteAppFunctionCallback>(), TEST_CALLING_UID) + mSafeCallback.setExecutionStartTimeAfterBindMillis(TEST_EXECUTION_TIME_AFTER_BIND_MILLIS) + } + + @Test + fun testOnSuccess_logsSuccessResponse() { + val response = + ExecuteAppFunctionResponse(GenericDocument.Builder<GenericDocument.Builder<*>>("", "", "") + .setPropertyLong("longProperty", 42L).setPropertyString("stringProperty", "text").build()) + + mSafeCallback.onResult(response) + + ExtendedMockito.verify { + AppFunctionsStatsLog.write( + /* atomId= */ eq<Int>(AppFunctionsStatsLog.APP_FUNCTIONS_REQUEST_REPORTED), + /* callerPackageUid= */ eq<Int>(TEST_CALLING_UID), + /* targetPackageUid= */ eq<Int>(TEST_TARGET_UID), + /* errorCode= */ eq<Int>(AppFunctionsLoggerWrapper.SUCCESS_RESPONSE_CODE), + /* requestSizeBytes= */ eq<Int>(mRequestInternal.clientRequest.requestDataSize), + /* responseSizeBytes= */ eq<Int>(response.responseDataSize), + /* requestDurationMs= */ eq<Long>(TEST_EXPECTED_E2E_DURATION_MILLIS), + /* requestOverheadMs= */ eq<Long>(TEST_EXPECTED_OVERHEAD_DURATION_MILLIS) + ) + } + } + + @Test + fun testOnError_logsFailureResponse() { + mSafeCallback.onError(AppFunctionException(AppFunctionException.ERROR_DENIED, "Error: permission denied")) + + ExtendedMockito.verify { + AppFunctionsStatsLog.write( + /* atomId= */ eq<Int>(AppFunctionsStatsLog.APP_FUNCTIONS_REQUEST_REPORTED), + /* callerPackageUid= */ eq<Int>(TEST_CALLING_UID), + /* targetPackageUid= */ eq<Int>(TEST_TARGET_UID), + /* errorCode= */ eq<Int>(AppFunctionException.ERROR_DENIED), + /* requestSizeBytes= */ eq<Int>(mRequestInternal.clientRequest.requestDataSize), + /* responseSizeBytes= */ eq<Int>(0), + /* requestDurationMs= */ eq<Long>(TEST_EXPECTED_E2E_DURATION_MILLIS), + /* requestOverheadMs= */ eq<Long>(TEST_EXPECTED_OVERHEAD_DURATION_MILLIS) + ) + } + } + + private companion object { + const val TEST_CALLING_PKG = "com.android.trusted.caller" + const val TEST_CALLING_UID = 12345 + const val TEST_TARGET_PACKAGE = "com.android.trusted.target" + const val TEST_TARGET_UID = 54321 + const val TEST_FUNCTION_ID = "com.android.valid.target.doSomething" + + const val TEST_INITIAL_REQUEST_TIME_MILLIS = 10L + const val TEST_EXECUTION_TIME_AFTER_BIND_MILLIS = 20L + const val TEST_CURRENT_TIME_MILLIS = 50L + const val TEST_EXPECTED_E2E_DURATION_MILLIS = + TEST_CURRENT_TIME_MILLIS - TEST_INITIAL_REQUEST_TIME_MILLIS + const val TEST_EXPECTED_OVERHEAD_DURATION_MILLIS = + TEST_EXECUTION_TIME_AFTER_BIND_MILLIS - TEST_INITIAL_REQUEST_TIME_MILLIS + } +} diff --git a/services/tests/displayservicetests/src/com/android/server/display/BrightnessSynchronizerTest.java b/services/tests/displayservicetests/src/com/android/server/display/BrightnessSynchronizerTest.java index a8708f9f9e6e..3449c36c3b7f 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/BrightnessSynchronizerTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/BrightnessSynchronizerTest.java @@ -224,7 +224,7 @@ public class BrightnessSynchronizerTest { mSynchronizer.startSynchronizing(); verify(mDisplayManagerMock).registerDisplayListener(mDisplayListenerCaptor.capture(), isA(Handler.class), eq(0L), - eq(DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS)); + eq(DisplayManager.PRIVATE_EVENT_TYPE_DISPLAY_BRIGHTNESS)); mDisplayListener = mDisplayListenerCaptor.getValue(); verify(mContentResolverSpy).registerContentObserver(eq(BRIGHTNESS_URI), eq(false), diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java index a4dfecb8ed96..7f12e9cf145a 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java @@ -1401,33 +1401,9 @@ public final class DisplayPowerControllerTest { } @Test - public void testRampRateForHdrContent_HdrClamperOff() { - float hdrBrightness = 0.8f; - float clampedBrightness = 0.6f; - float transitionRate = 1.5f; - - DisplayPowerRequest dpr = new DisplayPowerRequest(); - when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); - when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(.2f); - when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(.1f); - when(mHolder.hbmController.getHighBrightnessMode()).thenReturn( - BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR); - when(mHolder.hbmController.getHdrBrightnessValue()).thenReturn(hdrBrightness); - when(mHolder.hdrClamper.getMaxBrightness()).thenReturn(clampedBrightness); - when(mHolder.hdrClamper.getTransitionRate()).thenReturn(transitionRate); - - mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - advanceTime(1); // Run updatePowerState - - verify(mHolder.animator, atLeastOnce()).animateTo(eq(hdrBrightness), anyFloat(), - eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); - } - - @Test public void testRampRateForHdrContent_HdrClamperOn() { float clampedBrightness = 0.6f; float transitionRate = 1.5f; - when(mDisplayManagerFlagsMock.isHdrClamperEnabled()).thenReturn(true); mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ true); DisplayPowerRequest dpr = new DisplayPowerRequest(); @@ -2631,6 +2607,8 @@ public final class DisplayPowerControllerTest { BrightnessClamperController clamperController = mock(BrightnessClamperController.class); when(hbmController.getCurrentBrightnessMax()).thenReturn(PowerManager.BRIGHTNESS_MAX); + when(hdrClamper.clamp(anyFloat())).thenAnswer( + invocation -> invocation.getArgument(0)); when(clamperController.clamp(any(), any(), anyFloat(), anyBoolean(), anyInt())).thenAnswer( invocation -> DisplayBrightnessState.Builder.from(mDisplayBrightnessState) diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java index 4e0bab8bf4bd..f154dbcee21a 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java @@ -1225,8 +1225,8 @@ public class DisplayModeDirectorTest { ArgumentCaptor.forClass(DisplayListener.class); verify(mInjector).registerDisplayListener(displayListenerCaptor.capture(), any(Handler.class), - eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED), - eq(DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS)); + eq(DisplayManager.EVENT_TYPE_DISPLAY_CHANGED), + eq(DisplayManager.PRIVATE_EVENT_TYPE_DISPLAY_BRIGHTNESS)); DisplayListener displayListener = displayListenerCaptor.getValue(); setBrightness(10, 10, displayListener); @@ -1256,8 +1256,8 @@ public class DisplayModeDirectorTest { ArgumentCaptor.forClass(DisplayListener.class); verify(mInjector).registerDisplayListener(displayListenerCaptor.capture(), any(Handler.class), - eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED), - eq(DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS)); + eq(DisplayManager.EVENT_TYPE_DISPLAY_CHANGED), + eq(DisplayManager.PRIVATE_EVENT_TYPE_DISPLAY_BRIGHTNESS)); DisplayListener displayListener = displayListenerCaptor.getValue(); setBrightness(10, 10, displayListener); @@ -1291,8 +1291,8 @@ public class DisplayModeDirectorTest { ArgumentCaptor.forClass(DisplayListener.class); verify(mInjector).registerDisplayListener(displayListenerCaptor.capture(), any(Handler.class), - eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED), - eq(DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS)); + eq(DisplayManager.EVENT_TYPE_DISPLAY_CHANGED), + eq(DisplayManager.PRIVATE_EVENT_TYPE_DISPLAY_BRIGHTNESS)); DisplayListener displayListener = displayListenerCaptor.getValue(); setBrightness(10, 10, displayListener); @@ -1325,8 +1325,8 @@ public class DisplayModeDirectorTest { ArgumentCaptor.forClass(DisplayListener.class); verify(mInjector).registerDisplayListener(displayListenerCaptor.capture(), any(Handler.class), - eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED), - eq(DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS)); + eq(DisplayManager.EVENT_TYPE_DISPLAY_CHANGED), + eq(DisplayManager.PRIVATE_EVENT_TYPE_DISPLAY_BRIGHTNESS)); DisplayListener displayListener = displayListenerCaptor.getValue(); ArgumentCaptor<SensorEventListener> sensorListenerCaptor = @@ -1404,8 +1404,8 @@ public class DisplayModeDirectorTest { ArgumentCaptor.forClass(DisplayListener.class); verify(mInjector).registerDisplayListener(displayListenerCaptor.capture(), any(Handler.class), - eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED), - eq(DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS)); + eq(DisplayManager.EVENT_TYPE_DISPLAY_CHANGED), + eq(DisplayManager.PRIVATE_EVENT_TYPE_DISPLAY_BRIGHTNESS)); DisplayListener displayListener = displayListenerCaptor.getValue(); ArgumentCaptor<SensorEventListener> sensorListenerCaptor = @@ -1464,8 +1464,8 @@ public class DisplayModeDirectorTest { ArgumentCaptor.forClass(DisplayListener.class); verify(mInjector).registerDisplayListener(displayListenerCaptor.capture(), any(Handler.class), - eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED), - eq(DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS)); + eq(DisplayManager.EVENT_TYPE_DISPLAY_CHANGED), + eq(DisplayManager.PRIVATE_EVENT_TYPE_DISPLAY_BRIGHTNESS)); DisplayListener displayListener = displayListenerCaptor.getValue(); ArgumentCaptor<SensorEventListener> listenerCaptor = @@ -1630,8 +1630,8 @@ public class DisplayModeDirectorTest { ArgumentCaptor.forClass(DisplayListener.class); verify(mInjector).registerDisplayListener(displayListenerCaptor.capture(), any(Handler.class), - eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED), - eq(DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS)); + eq(DisplayManager.EVENT_TYPE_DISPLAY_CHANGED), + eq(DisplayManager.PRIVATE_EVENT_TYPE_DISPLAY_BRIGHTNESS)); DisplayListener displayListener = displayListenerCaptor.getValue(); // Get the sensor listener so that we can give it new light sensor events @@ -1730,8 +1730,8 @@ public class DisplayModeDirectorTest { ArgumentCaptor.forClass(DisplayListener.class); verify(mInjector).registerDisplayListener(displayListenerCaptor.capture(), any(Handler.class), - eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED), - eq(DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS)); + eq(DisplayManager.EVENT_TYPE_DISPLAY_CHANGED), + eq(DisplayManager.PRIVATE_EVENT_TYPE_DISPLAY_BRIGHTNESS)); DisplayListener displayListener = displayListenerCaptor.getValue(); // Get the sensor listener so that we can give it new light sensor events @@ -2814,9 +2814,9 @@ public class DisplayModeDirectorTest { ArgumentCaptor.forClass(DisplayListener.class); verify(mInjector, times(2)).registerDisplayListener(DisplayCaptor.capture(), any(Handler.class), - eq(DisplayManager.EVENT_FLAG_DISPLAY_ADDED - | DisplayManager.EVENT_FLAG_DISPLAY_CHANGED - | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED)); + eq(DisplayManager.EVENT_TYPE_DISPLAY_ADDED + | DisplayManager.EVENT_TYPE_DISPLAY_CHANGED + | DisplayManager.EVENT_TYPE_DISPLAY_REMOVED)); DisplayListener displayListener = DisplayCaptor.getAllValues().get(0); // Verify that there is no proximity vote initially @@ -2877,8 +2877,8 @@ public class DisplayModeDirectorTest { ArgumentCaptor<DisplayListener> captor = ArgumentCaptor.forClass(DisplayListener.class); verify(mInjector).registerDisplayListener(captor.capture(), any(Handler.class), - eq(DisplayManager.EVENT_FLAG_DISPLAY_REMOVED), - eq(DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS)); + eq(DisplayManager.EVENT_TYPE_DISPLAY_REMOVED), + eq(DisplayManager.PRIVATE_EVENT_TYPE_DISPLAY_BRIGHTNESS)); DisplayListener listener = captor.getValue(); // Specify Limitation @@ -3000,8 +3000,8 @@ public class DisplayModeDirectorTest { ArgumentCaptor<DisplayListener> captor = ArgumentCaptor.forClass(DisplayListener.class); verify(mInjector).registerDisplayListener(captor.capture(), any(Handler.class), - eq(DisplayManager.EVENT_FLAG_DISPLAY_REMOVED), - eq(DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS)); + eq(DisplayManager.EVENT_TYPE_DISPLAY_REMOVED), + eq(DisplayManager.PRIVATE_EVENT_TYPE_DISPLAY_BRIGHTNESS)); DisplayListener listener = captor.getValue(); final int initialRefreshRate = 60; @@ -3075,8 +3075,8 @@ public class DisplayModeDirectorTest { ArgumentCaptor<DisplayListener> captor = ArgumentCaptor.forClass(DisplayListener.class); verify(mInjector).registerDisplayListener(captor.capture(), any(Handler.class), - eq(DisplayManager.EVENT_FLAG_DISPLAY_REMOVED), - eq(DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS)); + eq(DisplayManager.EVENT_TYPE_DISPLAY_REMOVED), + eq(DisplayManager.PRIVATE_EVENT_TYPE_DISPLAY_BRIGHTNESS)); DisplayListener listener = captor.getValue(); // Specify Limitation for different display @@ -3115,8 +3115,8 @@ public class DisplayModeDirectorTest { ArgumentCaptor<DisplayListener> captor = ArgumentCaptor.forClass(DisplayListener.class); verify(mInjector).registerDisplayListener(captor.capture(), any(Handler.class), - eq(DisplayManager.EVENT_FLAG_DISPLAY_REMOVED), - eq(DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS)); + eq(DisplayManager.EVENT_TYPE_DISPLAY_REMOVED), + eq(DisplayManager.PRIVATE_EVENT_TYPE_DISPLAY_BRIGHTNESS)); DisplayListener listener = captor.getValue(); // Specify Limitation @@ -3200,8 +3200,8 @@ public class DisplayModeDirectorTest { ArgumentCaptor<DisplayListener> captor = ArgumentCaptor.forClass(DisplayListener.class); verify(mInjector).registerDisplayListener(captor.capture(), any(Handler.class), - eq(DisplayManager.EVENT_FLAG_DISPLAY_REMOVED), - eq(DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS)); + eq(DisplayManager.EVENT_TYPE_DISPLAY_REMOVED), + eq(DisplayManager.PRIVATE_EVENT_TYPE_DISPLAY_BRIGHTNESS)); DisplayListener listener = captor.getValue(); // Specify Sunlight limitations @@ -3239,8 +3239,8 @@ public class DisplayModeDirectorTest { ArgumentCaptor<DisplayListener> captor = ArgumentCaptor.forClass(DisplayListener.class); verify(mInjector).registerDisplayListener(captor.capture(), any(Handler.class), - eq(DisplayManager.EVENT_FLAG_DISPLAY_REMOVED), - eq(DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS)); + eq(DisplayManager.EVENT_TYPE_DISPLAY_REMOVED), + eq(DisplayManager.PRIVATE_EVENT_TYPE_DISPLAY_BRIGHTNESS)); DisplayListener listener = captor.getValue(); // Specify Limitation for different display diff --git a/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java b/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java index de6f9bd7527a..bd15bd058fcc 100644 --- a/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java +++ b/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java @@ -391,19 +391,19 @@ public class HintManagerServiceTest { makeSessionCreationConfig(SESSION_TIDS_A, DEFAULT_TARGET_DURATION); IHintSession a = service.getBinderServiceInstance().createHintSessionWithConfig(token, - SessionTag.OTHER, creationConfig, new SessionConfig()); + SessionTag.OTHER, creationConfig, new SessionConfig()).session; assertNotNull(a); creationConfig.tids = SESSION_TIDS_B; creationConfig.targetWorkDurationNanos = DOUBLED_TARGET_DURATION; IHintSession b = service.getBinderServiceInstance().createHintSessionWithConfig(token, - SessionTag.OTHER, creationConfig, new SessionConfig()); + SessionTag.OTHER, creationConfig, new SessionConfig()).session; assertNotEquals(a, b); creationConfig.tids = SESSION_TIDS_C; creationConfig.targetWorkDurationNanos = 0L; IHintSession c = service.getBinderServiceInstance().createHintSessionWithConfig(token, - SessionTag.OTHER, creationConfig, new SessionConfig()); + SessionTag.OTHER, creationConfig, new SessionConfig()).session; assertNotNull(c); verify(mNativeWrapperMock, times(3)).halCreateHintSession(anyInt(), anyInt(), any(int[].class), anyLong()); @@ -418,7 +418,7 @@ public class HintManagerServiceTest { SessionConfig config = new SessionConfig(); IHintSession a = service.getBinderServiceInstance().createHintSessionWithConfig(token, - SessionTag.OTHER, creationConfig, config); + SessionTag.OTHER, creationConfig, config).session; assertNotNull(a); assertEquals(SESSION_IDS[0], config.id); @@ -426,7 +426,7 @@ public class HintManagerServiceTest { creationConfig.tids = SESSION_TIDS_B; creationConfig.targetWorkDurationNanos = DOUBLED_TARGET_DURATION; IHintSession b = service.getBinderServiceInstance().createHintSessionWithConfig(token, - SessionTag.APP, creationConfig, config2); + SessionTag.APP, creationConfig, config2).session; assertNotEquals(a, b); assertEquals(SESSION_IDS[1], config2.id); @@ -434,7 +434,7 @@ public class HintManagerServiceTest { creationConfig.tids = SESSION_TIDS_C; creationConfig.targetWorkDurationNanos = 0L; IHintSession c = service.getBinderServiceInstance().createHintSessionWithConfig(token, - SessionTag.GAME, creationConfig, config3); + SessionTag.GAME, creationConfig, config3).session; assertNotNull(c); assertEquals(SESSION_IDS[2], config3.id); verify(mNativeWrapperMock, times(3)).halCreateHintSessionWithConfig(anyInt(), anyInt(), @@ -465,16 +465,14 @@ public class HintManagerServiceTest { SessionConfig config = new SessionConfig(); IHintSession a = service.getBinderServiceInstance().createHintSessionWithConfig(token, - SessionTag.OTHER, creationConfig, config); + SessionTag.OTHER, creationConfig, config).session; assertNotNull(a); assertEquals(sessionId1, config.id); creationConfig.tids = createThreads(1, stopLatch1); - assertThrows(IllegalArgumentException.class, () -> { - service.getBinderServiceInstance().createHintSessionWithConfig(token, - SessionTag.OTHER, creationConfig, config); - }); + assertEquals(service.getBinderServiceInstance().createHintSessionWithConfig(token, + SessionTag.OTHER, creationConfig, config).pipelineThreadLimitExceeded, true); } @Test @@ -486,7 +484,7 @@ public class HintManagerServiceTest { AppHintSession a = (AppHintSession) service.getBinderServiceInstance() .createHintSessionWithConfig(token, SessionTag.OTHER, - creationConfig, new SessionConfig()); + creationConfig, new SessionConfig()).session; // Set session to background and calling updateHintAllowedByProcState() would invoke // pause(); @@ -526,7 +524,7 @@ public class HintManagerServiceTest { makeSessionCreationConfig(SESSION_TIDS_A, DEFAULT_TARGET_DURATION); IHintSession a = service.getBinderServiceInstance().createHintSessionWithConfig(token, - SessionTag.OTHER, creationConfig, new SessionConfig()); + SessionTag.OTHER, creationConfig, new SessionConfig()).session; a.close(); verify(mNativeWrapperMock, times(1)).halCloseHintSession(anyLong()); @@ -540,16 +538,12 @@ public class HintManagerServiceTest { makeSessionCreationConfig(SESSION_TIDS_A, DEFAULT_TARGET_DURATION); IHintSession a = service.getBinderServiceInstance().createHintSessionWithConfig(token, - SessionTag.OTHER, creationConfig, new SessionConfig()); + SessionTag.OTHER, creationConfig, new SessionConfig()).session; assertThrows(IllegalArgumentException.class, () -> { a.updateTargetWorkDuration(-1L); }); - assertThrows(IllegalArgumentException.class, () -> { - a.updateTargetWorkDuration(0L); - }); - a.updateTargetWorkDuration(100L); verify(mNativeWrapperMock, times(1)).halUpdateTargetWorkDuration(anyLong(), eq(100L)); } @@ -563,7 +557,7 @@ public class HintManagerServiceTest { AppHintSession a = (AppHintSession) service.getBinderServiceInstance() .createHintSessionWithConfig(token, SessionTag.OTHER, - creationConfig, new SessionConfig()); + creationConfig, new SessionConfig()).session; a.updateTargetWorkDuration(100L); a.reportActualWorkDuration(DURATIONS_THREE, TIMESTAMPS_THREE); @@ -608,7 +602,7 @@ public class HintManagerServiceTest { AppHintSession a = (AppHintSession) service.getBinderServiceInstance() .createHintSessionWithConfig(token, SessionTag.OTHER, - creationConfig, new SessionConfig()); + creationConfig, new SessionConfig()).session; a.sendHint(PerformanceHintManager.Session.CPU_LOAD_RESET); verify(mNativeWrapperMock, times(1)).halSendHint(anyLong(), @@ -637,7 +631,7 @@ public class HintManagerServiceTest { AppHintSession a = (AppHintSession) service.getBinderServiceInstance() .createHintSessionWithConfig(token, SessionTag.OTHER, - creationConfig, new SessionConfig()); + creationConfig, new SessionConfig()).session; service.mUidObserver.onUidStateChanged( a.mUid, ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND, 0, 0); @@ -661,7 +655,7 @@ public class HintManagerServiceTest { AppHintSession a = (AppHintSession) service.getBinderServiceInstance() .createHintSessionWithConfig(token, SessionTag.OTHER, - creationConfig, new SessionConfig()); + creationConfig, new SessionConfig()).session; service.mUidObserver.onUidStateChanged( a.mUid, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND, 0, 0); @@ -677,7 +671,7 @@ public class HintManagerServiceTest { AppHintSession a = (AppHintSession) service.getBinderServiceInstance() .createHintSessionWithConfig(token, SessionTag.OTHER, - creationConfig, new SessionConfig()); + creationConfig, new SessionConfig()).session; a.updateTargetWorkDuration(100L); @@ -717,7 +711,7 @@ public class HintManagerServiceTest { makeSessionCreationConfig(tids1, DEFAULT_TARGET_DURATION); AppHintSession session1 = (AppHintSession) service.getBinderServiceInstance() .createHintSessionWithConfig(token, SessionTag.OTHER, - creationConfig, new SessionConfig()); + creationConfig, new SessionConfig()).session; assertNotNull(session1); // trigger UID state change by making the process foreground->background, but because the @@ -754,7 +748,7 @@ public class HintManagerServiceTest { makeSessionCreationConfig(tids1, DEFAULT_TARGET_DURATION); AppHintSession session1 = (AppHintSession) service.getBinderServiceInstance() .createHintSessionWithConfig(token, SessionTag.OTHER, - creationConfig, new SessionConfig()); + creationConfig, new SessionConfig()).session; assertNotNull(session1); // let all session 1 threads to exit and the cleanup should force pause the session 1 @@ -865,7 +859,7 @@ public class HintManagerServiceTest { AppHintSession a = (AppHintSession) service.getBinderServiceInstance() .createHintSessionWithConfig(token, SessionTag.OTHER, - creationConfig, new SessionConfig()); + creationConfig, new SessionConfig()).session; a.setMode(0, true); verify(mNativeWrapperMock, times(1)).halSetMode(anyLong(), @@ -885,7 +879,7 @@ public class HintManagerServiceTest { AppHintSession a = (AppHintSession) service.getBinderServiceInstance() .createHintSessionWithConfig(token, SessionTag.OTHER, - creationConfig, new SessionConfig()); + creationConfig, new SessionConfig()).session; // Set session to background, then the duration would not be updated. service.mUidObserver.onUidStateChanged( @@ -906,7 +900,7 @@ public class HintManagerServiceTest { AppHintSession a = (AppHintSession) service.getBinderServiceInstance() .createHintSessionWithConfig(token, SessionTag.OTHER, - creationConfig, new SessionConfig()); + creationConfig, new SessionConfig()).session; assertThrows(IllegalArgumentException.class, () -> { a.setMode(-1, true); @@ -923,7 +917,7 @@ public class HintManagerServiceTest { AppHintSession a = (AppHintSession) service.getBinderServiceInstance() .createHintSessionWithConfig(token, SessionTag.OTHER, - creationConfig, new SessionConfig()); + creationConfig, new SessionConfig()).session; assertNotNull(a); verify(mNativeWrapperMock, times(1)).halSetMode(anyLong(), eq(0), eq(true)); @@ -1124,7 +1118,7 @@ public class HintManagerServiceTest { AppHintSession a = (AppHintSession) service.getBinderServiceInstance() .createHintSessionWithConfig(token, SessionTag.OTHER, - creationConfig, new SessionConfig()); + creationConfig, new SessionConfig()).session; // we will start some threads and get their valid TIDs to update int threadCount = 3; // the list of TIDs @@ -1194,7 +1188,7 @@ public class HintManagerServiceTest { AppHintSession a = (AppHintSession) service.getBinderServiceInstance() .createHintSessionWithConfig(token, SessionTag.OTHER, - creationConfig, new SessionConfig()); + creationConfig, new SessionConfig()).session; a.updateTargetWorkDuration(100L); a.reportActualWorkDuration2(WORK_DURATIONS_FIVE); diff --git a/tests/Input/src/com/android/test/input/UinputRecordingIntegrationTests.kt b/tests/Input/src/com/android/test/input/UinputRecordingIntegrationTests.kt index 1811c461e23f..0b281d8d39e2 100644 --- a/tests/Input/src/com/android/test/input/UinputRecordingIntegrationTests.kt +++ b/tests/Input/src/com/android/test/input/UinputRecordingIntegrationTests.kt @@ -21,12 +21,12 @@ import android.cts.input.EventVerifier import android.graphics.PointF import android.hardware.input.InputManager import android.os.ParcelFileDescriptor +import android.server.wm.CtsWindowInfoUtils.waitForWindowOnTop import android.util.Log import android.util.Size import android.view.InputEvent import android.view.MotionEvent import androidx.test.platform.app.InstrumentationRegistry -import android.server.wm.CtsWindowInfoUtils.waitForStableWindowGeometry import com.android.cts.input.BatchedEventSplitter import com.android.cts.input.InputJsonParser import com.android.cts.input.VirtualDisplayActivityScenario @@ -37,7 +37,6 @@ import com.android.cts.input.inputeventmatchers.withMotionAction import com.android.cts.input.inputeventmatchers.withPressure import com.android.cts.input.inputeventmatchers.withRawCoords import com.android.cts.input.inputeventmatchers.withSource -import java.time.Duration import junit.framework.Assert.fail import org.hamcrest.Matchers.allOf import org.junit.Before @@ -115,7 +114,7 @@ class UinputRecordingIntegrationTests { testName, size = testData.displaySize ).use { scenario -> - waitForStableWindowGeometry(Duration.ofSeconds(5)) + waitForWindowOnTop(scenario.activity.window) scenario.activity.window.decorView.requestUnbufferedDispatch(INPUT_DEVICE_SOURCE_ALL) try { |