Merge "Remove slider animation when set model first time" into main
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 2da7147..ec10913 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -768,6 +768,10 @@
INotificationManager service = service();
String sender = mContext.getPackageName();
+ if (discardNotify(mContext.getUser(), targetPackage, tag, id, notification)) {
+ return;
+ }
+
try {
if (localLOGV) Log.v(TAG, sender + ": notify(" + id + ", " + notification + ")");
service.enqueueNotificationWithTag(targetPackage, sender, tag, id,
@@ -918,6 +922,10 @@
* @param id An identifier for this notification.
*/
public void cancelAsPackage(@NonNull String targetPackage, @Nullable String tag, int id) {
+ if (discardCancel(mContext.getUser(), targetPackage, tag, id)) {
+ return;
+ }
+
INotificationManager service = service();
try {
service.cancelNotificationWithTag(targetPackage, mContext.getOpPackageName(),
@@ -981,16 +989,20 @@
*/
public void cancelAll()
{
+ String pkg = mContext.getPackageName();
+ UserHandle user = mContext.getUser();
+
if (Flags.nmBinderPerfThrottleNotify()) {
synchronized (mThrottleLock) {
for (NotificationKey key : mKnownNotifications.snapshot().keySet()) {
- mKnownNotifications.put(key, KNOWN_STATUS_CANCELLED);
+ if (key.pkg.equals(pkg) && key.user.equals(user)) {
+ mKnownNotifications.put(key, KNOWN_STATUS_CANCELLED);
+ }
}
}
}
INotificationManager service = service();
- String pkg = mContext.getPackageName();
if (localLOGV) Log.v(TAG, pkg + ": cancelAll()");
try {
service.cancelAllNotifications(pkg, mContext.getUserId());
@@ -1014,7 +1026,7 @@
public void setNotificationDelegate(@Nullable String delegate) {
INotificationManager service = service();
String pkg = mContext.getPackageName();
- if (localLOGV) Log.v(TAG, pkg + ": cancelAll()");
+ if (localLOGV) Log.v(TAG, pkg + ": setNotificationDelegate()");
try {
service.setNotificationDelegate(pkg, delegate);
} catch (RemoteException e) {
diff --git a/core/java/android/app/appfunctions/SafeOneTimeExecuteAppFunctionCallback.java b/core/java/android/app/appfunctions/SafeOneTimeExecuteAppFunctionCallback.java
index e527de2..88001fc 100644
--- a/core/java/android/app/appfunctions/SafeOneTimeExecuteAppFunctionCallback.java
+++ b/core/java/android/app/appfunctions/SafeOneTimeExecuteAppFunctionCallback.java
@@ -19,7 +19,6 @@
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 @@
@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 @@
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 @@
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 @@
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 @@
* 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 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/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl
index 55957bf..7f14925 100644
--- a/core/java/android/content/pm/ILauncherApps.aidl
+++ b/core/java/android/content/pm/ILauncherApps.aidl
@@ -83,8 +83,6 @@
ParceledListSlice getShortcuts(String callingPackage, in ShortcutQueryWrapper query,
in UserHandle user);
- void getShortcutsAsync(String callingPackage, in ShortcutQueryWrapper query,
- in UserHandle user, in AndroidFuture<List<ShortcutInfo>> cb);
void pinShortcuts(String callingPackage, String packageName, in List<String> shortcutIds,
in UserHandle user);
boolean startShortcut(String callingPackage, String packageName, String featureId, String id,
diff --git a/core/java/android/content/pm/IShortcutService.aidl b/core/java/android/content/pm/IShortcutService.aidl
index 86087cb..371f645 100644
--- a/core/java/android/content/pm/IShortcutService.aidl
+++ b/core/java/android/content/pm/IShortcutService.aidl
@@ -21,8 +21,6 @@
import android.content.pm.ParceledListSlice;
import android.content.pm.ShortcutInfo;
-import com.android.internal.infra.AndroidFuture;
-
/** {@hide} */
interface IShortcutService {
@@ -38,11 +36,11 @@
boolean updateShortcuts(String packageName, in ParceledListSlice shortcuts, int userId);
- void requestPinShortcut(String packageName, in ShortcutInfo shortcut,
- in IntentSender resultIntent, int userId, in AndroidFuture<String> ret);
+ boolean requestPinShortcut(String packageName, in ShortcutInfo shortcut,
+ in IntentSender resultIntent, int userId);
- void createShortcutResultIntent(String packageName, in ShortcutInfo shortcut, int userId,
- in AndroidFuture<Intent> ret);
+ Intent createShortcutResultIntent(String packageName, in ShortcutInfo shortcut,
+ int userId);
void disableShortcuts(String packageName, in List<String> shortcutIds,
CharSequence disabledMessage, int disabledMessageResId, int userId);
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 26b8356..a0c0f12 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -464,17 +464,6 @@
public static final int FLAG_GET_KEY_FIELDS_ONLY = 1 << 2;
/**
- * Includes shortcuts from persistence layer in the search result.
- *
- * <p>The caller should make the query on a worker thread since accessing persistence layer
- * is considered asynchronous.
- *
- * @hide
- */
- @SystemApi
- public static final int FLAG_GET_PERSISTED_DATA = 1 << 12;
-
- /**
* Populate the persons field in the result. See {@link ShortcutInfo#getPersons()}.
*
* <p>The caller must have the system {@code ACCESS_SHORTCUTS} permission.
@@ -485,6 +474,17 @@
@RequiresPermission(android.Manifest.permission.ACCESS_SHORTCUTS)
public static final int FLAG_GET_PERSONS_DATA = 1 << 11;
+ /**
+ * Includes shortcuts from persistence layer in the search result.
+ *
+ * <p>The caller should make the query on a worker thread since accessing persistence layer
+ * is considered asynchronous.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int FLAG_GET_PERSISTED_DATA = 1 << 12;
+
/** @hide */
@IntDef(flag = true, prefix = { "FLAG_" }, value = {
FLAG_MATCH_DYNAMIC,
@@ -1500,9 +1500,6 @@
@NonNull UserHandle user) {
logErrorForInvalidProfileAccess(user);
try {
- if ((query.mQueryFlags & ShortcutQuery.FLAG_GET_PERSISTED_DATA) != 0) {
- return getShortcutsBlocked(query, user);
- }
// Note this is the only case we need to update the disabled message for shortcuts
// that weren't restored.
// The restore problem messages are only shown by the user, and publishers will never
@@ -1517,22 +1514,6 @@
}
}
- private List<ShortcutInfo> getShortcutsBlocked(@NonNull ShortcutQuery query,
- @NonNull UserHandle user) {
- logErrorForInvalidProfileAccess(user);
- final AndroidFuture<List<ShortcutInfo>> future = new AndroidFuture<>();
- future.thenApply(this::maybeUpdateDisabledMessage);
- try {
- mService.getShortcutsAsync(mContext.getPackageName(),
- new ShortcutQueryWrapper(query), user, future);
- return future.get();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- } catch (InterruptedException | ExecutionException e) {
- throw new RuntimeException(e);
- }
- }
-
/**
* @hide // No longer used. Use getShortcuts() instead. Kept for unit tests.
*/
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index cd62573..d64ef75 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -3617,7 +3617,9 @@
* automatically fetched and installed when installing an app that wants to use these
* dependencies.
*
- * <p> This feature is enabled by default.
+ * <p> This feature is enabled by default. Note that in the case of a multi-package
+ * installation session, no dependencies will be automatically installed even if this field
+ * is set to true.
*
* @param enableAutoInstallDependencies {@code true} to enable auto-installation of missing
* SDK or static shared library dependencies,
@@ -4574,6 +4576,9 @@
* Check whether missing SDK or static shared library dependencies should be automatically
* fetched and installed when installing an app that wants to use these dependencies.
*
+ * <p> Note that in the case of a multi-package installation session, no dependencies will
+ * be automatically installed even if this method returns true.
+ *
* @return true if the dependencies will be auto-installed, false otherwise.
*/
@FlaggedApi(Flags.FLAG_SDK_DEPENDENCY_INSTALLER)
diff --git a/core/java/android/content/pm/ShortcutManager.java b/core/java/android/content/pm/ShortcutManager.java
index 3514914..683f312 100644
--- a/core/java/android/content/pm/ShortcutManager.java
+++ b/core/java/android/content/pm/ShortcutManager.java
@@ -44,7 +44,6 @@
import android.os.ServiceManager;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.infra.AndroidFuture;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -598,10 +597,8 @@
public boolean requestPinShortcut(@NonNull ShortcutInfo shortcut,
@Nullable IntentSender resultIntent) {
try {
- AndroidFuture<String> ret = new AndroidFuture<>();
- mService.requestPinShortcut(mContext.getPackageName(), shortcut, resultIntent,
- injectMyUserId(), ret);
- return Boolean.parseBoolean(getFutureOrThrow(ret));
+ return mService.requestPinShortcut(mContext.getPackageName(), shortcut, resultIntent,
+ injectMyUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -626,11 +623,9 @@
*/
@WorkerThread
public Intent createShortcutResultIntent(@NonNull ShortcutInfo shortcut) {
- final AndroidFuture<Intent> ret = new AndroidFuture<>();
try {
- mService.createShortcutResultIntent(mContext.getPackageName(),
- shortcut, injectMyUserId(), ret);
- Intent result = getFutureOrThrow(ret);
+ Intent result = mService.createShortcutResultIntent(mContext.getPackageName(),
+ shortcut, injectMyUserId());
if (result != null) {
result.prepareToEnterProcess(LOCAL_FLAG_FROM_SYSTEM,
mContext.getAttributionSource());
@@ -793,21 +788,4 @@
throw e.rethrowFromSystemServer();
}
}
-
- private static <T> T getFutureOrThrow(@NonNull AndroidFuture<T> future) {
- try {
- return future.get();
- } catch (Throwable e) {
- if (e instanceof ExecutionException) {
- e = e.getCause();
- }
- if (e instanceof RuntimeException) {
- throw (RuntimeException) e;
- }
- if (e instanceof Error) {
- throw (Error) e;
- }
- throw new RuntimeException(e);
- }
- }
}
diff --git a/core/java/android/content/pm/ShortcutServiceInternal.java b/core/java/android/content/pm/ShortcutServiceInternal.java
index c811a47..d38b2f0 100644
--- a/core/java/android/content/pm/ShortcutServiceInternal.java
+++ b/core/java/android/content/pm/ShortcutServiceInternal.java
@@ -52,19 +52,6 @@
@Nullable List<LocusId> locusIds, @Nullable ComponentName componentName,
@ShortcutQuery.QueryFlags int flags, int userId, int callingPid, int callingUid);
- /**
- * Retrieves shortcuts asynchronously. Query will go through persistence layer (thus making the
- * call async) if querying by shortcutIds in a specific package; otherwise it's effectively the
- * same as calling {@link #getShortcuts}.
- */
- public abstract void
- getShortcutsAsync(int launcherUserId,
- @NonNull String callingPackage, long changedSince,
- @Nullable String packageName, @Nullable List<String> shortcutIds,
- @Nullable List<LocusId> locusIds, @Nullable ComponentName componentName,
- @ShortcutQuery.QueryFlags int flags, int userId, int callingPid, int callingUid,
- AndroidFuture<List<ShortcutInfo>> cb);
-
public abstract boolean
isPinnedByCaller(int launcherUserId, @NonNull String callingPackage,
@NonNull String packageName, @NonNull String id, int userId);
@@ -78,14 +65,6 @@
@NonNull String packageName, @NonNull String shortcutId, int userId,
int callingPid, int callingUid);
- /**
- * Retrieves the intents from a specified shortcut asynchronously.
- */
- public abstract void createShortcutIntentsAsync(
- int launcherUserId, @NonNull String callingPackage,
- @NonNull String packageName, @NonNull String shortcutId, int userId,
- int callingPid, int callingUid, @NonNull AndroidFuture<Intent[]> cb);
-
public abstract void addListener(@NonNull ShortcutChangeListener listener);
public abstract void addShortcutChangeCallback(
@@ -108,13 +87,6 @@
@NonNull String callingPackage,
@NonNull String packageName, @NonNull String shortcutId, int userId);
- /**
- * Retrieves a file descriptor from the icon in a specified shortcut asynchronously.
- */
- public abstract void getShortcutIconFdAsync(int launcherUserId, @NonNull String callingPackage,
- @NonNull String packageName, @NonNull String shortcutId, int userId,
- @NonNull AndroidFuture<ParcelFileDescriptor> cb);
-
public abstract boolean hasShortcutHostPermission(int launcherUserId,
@NonNull String callingPackage, int callingPid, int callingUid);
@@ -155,14 +127,6 @@
public abstract String getShortcutIconUri(int launcherUserId, @NonNull String launcherPackage,
@NonNull String packageName, @NonNull String shortcutId, int userId);
- /**
- * Retrieves the icon Uri of the shortcut asynchronously, and grants Uri read permission to the
- * caller.
- */
- public abstract void getShortcutIconUriAsync(int launcherUserId,
- @NonNull String launcherPackage, @NonNull String packageName,
- @NonNull String shortcutId, int userId, @NonNull AndroidFuture<String> cb);
-
public abstract boolean isSharingShortcut(int callingUserId, @NonNull String callingPackage,
@NonNull String packageName, @NonNull String shortcutId, int userId,
@NonNull IntentFilter filter);
diff --git a/core/java/android/os/IHintManager.aidl b/core/java/android/os/IHintManager.aidl
index 56a089a..5128e91 100644
--- a/core/java/android/os/IHintManager.aidl
+++ b/core/java/android/os/IHintManager.aidl
@@ -39,12 +39,18 @@
* 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/view/inputmethod/flags.aconfig b/core/java/android/view/inputmethod/flags.aconfig
index 41567fb..4258ca4 100644
--- a/core/java/android/view/inputmethod/flags.aconfig
+++ b/core/java/android/view/inputmethod/flags.aconfig
@@ -194,3 +194,14 @@
is_fixed_read_only: true
is_exported: true
}
+
+flag {
+ name: "fallback_display_for_secondary_user_on_secondary_display"
+ namespace: "input_method"
+ description: "Feature flag to fix the fallback display bug for visible background users"
+ bug: "383228193"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 9fe3fd6..7c75d7b 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -5820,8 +5820,13 @@
mActions.forEach(action -> {
if (viewId == action.mViewId
&& action instanceof SetOnClickResponse setOnClickResponse) {
- setOnClickResponse.mResponse.handleViewInteraction(
- player, params.handler);
+ final RemoteResponse response = setOnClickResponse.mResponse;
+ if (response.mFillIntent == null) {
+ response.mFillIntent = new Intent();
+ }
+ response.mFillIntent.putExtra(
+ "remotecompose_metadata", metadata);
+ response.handleViewInteraction(player, params.handler);
}
});
});
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index f346544..7eabd17 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -441,4 +441,12 @@
metadata {
purpose: PURPOSE_BUGFIX
}
-}
\ No newline at end of file
+}
+
+flag {
+ name: "port_window_size_animation"
+ namespace: "systemui"
+ description: "Port window-resize animation from legacy to shell"
+ bug: "384976265"
+}
+
diff --git a/core/java/com/android/internal/app/PlatLogoActivity.java b/core/java/com/android/internal/app/PlatLogoActivity.java
index b8f7a3d..5d55418 100644
--- a/core/java/com/android/internal/app/PlatLogoActivity.java
+++ b/core/java/com/android/internal/app/PlatLogoActivity.java
@@ -18,6 +18,9 @@
import static android.os.VibrationEffect.Composition.PRIMITIVE_SPIN;
+import static java.lang.Math.hypot;
+import static java.lang.Math.max;
+
import android.animation.ObjectAnimator;
import android.animation.TimeAnimator;
import android.annotation.SuppressLint;
@@ -26,9 +29,11 @@
import android.content.ActivityNotFoundException;
import android.content.ContentResolver;
import android.content.Intent;
+import android.content.pm.ActivityInfo;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
+import android.graphics.ColorSpace;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
@@ -72,7 +77,7 @@
private static final String EGG_UNLOCK_SETTING = "egg_mode_v";
private static final float MIN_WARP = 1f;
- private static final float MAX_WARP = 10f; // after all these years
+ private static final float MAX_WARP = 16f; // must go faster
private static final boolean FINISH_AFTER_NEXT_STAGE_LAUNCH = false;
private ImageView mLogo;
@@ -201,6 +206,9 @@
getWindow().setStatusBarColor(0);
getWindow().getDecorView().getWindowInsetsController().hide(WindowInsets.Type.systemBars());
+ // This will be silently ignored on displays that don't support HDR color, which is fine
+ getWindow().setColorMode(ActivityInfo.COLOR_MODE_HDR);
+
final ActionBar ab = getActionBar();
if (ab != null) ab.hide();
@@ -364,7 +372,7 @@
mPressureMin = Math.min(mPressureMin, touchData.getDouble("min"));
}
if (touchData.has("max")) {
- mPressureMax = Math.max(mPressureMax, touchData.getDouble("max"));
+ mPressureMax = max(mPressureMax, touchData.getDouble("max"));
}
if (mPressureMax >= 0) {
touchData.put("min", mPressureMin);
@@ -392,9 +400,11 @@
}
private static class Starfield extends Drawable {
- private static final int NUM_STARS = 34; // Build.VERSION_CODES.UPSIDE_DOWN_CAKE
+ private static final int NUM_STARS = 128;
- private static final int NUM_PLANES = 2;
+ private static final int NUM_PLANES = 4;
+
+ private static final float ROTATION = 45;
private final float[] mStars = new float[NUM_STARS * 4];
private float mVx, mVy;
private long mDt = 0;
@@ -403,7 +413,7 @@
private final Random mRng;
private final float mSize;
- private final Rect mSpace = new Rect();
+ private float mRadius = 0f;
private float mWarp = 1f;
private float mBuffer;
@@ -426,14 +436,15 @@
@Override
public void onBoundsChange(Rect bounds) {
- mSpace.set(bounds);
mBuffer = mSize * NUM_PLANES * 2 * MAX_WARP;
- mSpace.inset(-(int) mBuffer, -(int) mBuffer);
- final float w = mSpace.width();
- final float h = mSpace.height();
+ mRadius = ((float) hypot(bounds.width(), bounds.height()) / 2f) + mBuffer;
+ // I didn't clarify this the last time, but we store both the beginning and
+ // end of each star's trail in this data structure. When we're not in warp that means
+ // that we've got each star in there twice. It's fine, we're gonna move it off-screen
for (int i = 0; i < NUM_STARS; i++) {
- mStars[4 * i] = mRng.nextFloat() * w;
- mStars[4 * i + 1] = mRng.nextFloat() * h;
+ mStars[4 * i] = mRng.nextFloat() * 2 * mRadius - mRadius;
+ mStars[4 * i + 1] = mRng.nextFloat() * 2 * mRadius - mRadius;
+ // duplicate copy (for now)
mStars[4 * i + 2] = mStars[4 * i];
mStars[4 * i + 3] = mStars[4 * i + 1];
}
@@ -452,31 +463,47 @@
final boolean inWarp = mWarp > 1f;
- canvas.drawColor(Color.BLACK); // 0xFF16161D);
+ final float diameter = mRadius * 2f;
+ final float triameter = mRadius * 3f;
+
+ canvas.drawColor(Color.BLACK);
+
+ final float cx = getBounds().width() / 2f;
+ final float cy = getBounds().height() / 2f;
+ canvas.translate(cx, cy);
+
+ canvas.rotate(ROTATION);
if (mDt > 0 && mDt < 1000) {
canvas.translate(
- -(mBuffer) + mRng.nextFloat() * (mWarp - 1f),
- -(mBuffer) + mRng.nextFloat() * (mWarp - 1f)
+ mRng.nextFloat() * (mWarp - 1f),
+ mRng.nextFloat() * (mWarp - 1f)
);
- final float w = mSpace.width();
- final float h = mSpace.height();
for (int i = 0; i < NUM_STARS; i++) {
final int plane = (int) ((((float) i) / NUM_STARS) * NUM_PLANES) + 1;
- mStars[4 * i + 2] = (mStars[4 * i + 2] + dx * plane + w) % w;
- mStars[4 * i + 3] = (mStars[4 * i + 3] + dy * plane + h) % h;
- mStars[4 * i + 0] = inWarp ? mStars[4 * i + 2] - dx * mWarp * 2 * plane : -100;
- mStars[4 * i + 1] = inWarp ? mStars[4 * i + 3] - dy * mWarp * 2 * plane : -100;
+ mStars[4 * i + 2] = (mStars[4 * i + 2] + dx * plane + triameter) % diameter
+ - mRadius;
+ mStars[4 * i + 3] = (mStars[4 * i + 3] + dy * plane + triameter) % diameter
+ - mRadius;
+ mStars[4 * i + 0] = inWarp ? mStars[4 * i + 2] - dx * mWarp * plane : -10000;
+ mStars[4 * i + 1] = inWarp ? mStars[4 * i + 3] - dy * mWarp * plane : -10000;
}
}
final int slice = (mStars.length / NUM_PLANES / 4) * 4;
for (int p = 0; p < NUM_PLANES; p++) {
+ final float value = (p + 1f) / (NUM_PLANES - 1);
+ mStarPaint.setColor(packHdrColor(value, 1.0f));
mStarPaint.setStrokeWidth(mSize * (p + 1));
if (inWarp) {
canvas.drawLines(mStars, p * slice, slice, mStarPaint);
}
canvas.drawPoints(mStars, p * slice, slice, mStarPaint);
}
+
+ if (inWarp) {
+ final float frac = (mWarp - MIN_WARP) / (MAX_WARP - MIN_WARP);
+ canvas.drawColor(packHdrColor(2.0f, frac * frac));
+ }
}
@Override
@@ -497,5 +524,10 @@
public void update(long dt) {
mDt = dt;
}
+
+ private static final ColorSpace sSrgbExt = ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB);
+ public static long packHdrColor(float value, float alpha) {
+ return Color.valueOf(value, value, value, alpha, sSrgbExt).pack();
+ }
}
}
diff --git a/core/res/res/drawable-nodpi/platlogo.xml b/core/res/res/drawable-nodpi/platlogo.xml
index 822aa22..7432796 100644
--- a/core/res/res/drawable-nodpi/platlogo.xml
+++ b/core/res/res/drawable-nodpi/platlogo.xml
@@ -1,5 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
<!--
-Copyright (C) 2021 The Android Open Source Project
+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.
@@ -18,97 +19,129 @@
android:height="512dp"
android:viewportWidth="512"
android:viewportHeight="512">
- <!-- space -->
<path
- android:pathData="M256,446.36C229.63,446.36 212.19,428.06 194.8,397.94C177.41,367.82 91.54,219.08 74.14,188.96C56.75,158.84 49.63,134.59 62.81,111.75C76,88.91 100.56,82.95 135.35,82.95C170.13,82.95 341.87,82.95 376.65,82.95C411.44,82.95 436,88.91 449.19,111.75C462.37,134.59 455.25,158.84 437.86,188.96C420.46,219.08 334.59,367.82 317.2,397.94C299.81,428.06 282.37,446.36 256,446.36H256Z"
- android:fillColor="#202124"/>
- <group>
- <clip-path
- android:pathData="M256,446.36C229.63,446.36 212.19,428.06 194.8,397.94C177.41,367.82 91.54,219.08 74.14,188.96C56.75,158.84 49.63,134.59 62.81,111.75C76,88.91 100.56,82.95 135.35,82.95C170.13,82.95 341.87,82.95 376.65,82.95C411.44,82.95 436,88.91 449.19,111.75C462.37,134.59 455.25,158.84 437.86,188.96C420.46,219.08 334.59,367.82 317.2,397.94C299.81,428.06 282.37,446.36 256,446.36H256Z"/>
- <!-- thrust plume -->
- <path
- android:pathData="M253,153C249.82,187.48 225.67,262.17 167.98,285.04C110.3,307.92 73.96,318.12 63,320.36L256,399L449,320.36C438.04,318.12 401.7,307.92 344.02,285.04C286.33,262.17 262.18,187.48 259,153H256H253Z"
- android:fillColor="#C6FF00"
- android:fillType="evenOdd"/>
- <path
- android:pathData="M253,153C251.5,187.42 241.7,261.98 214.5,284.82C187.3,307.65 170.17,317.84 165,320.08L256,398.58L347,320.08C341.83,317.84 324.7,307.65 297.5,284.82C270.3,261.98 260.5,187.42 259,153H256H253Z"
- android:fillColor="#ffffff"
- android:fillType="evenOdd"/>
- <path
- android:pathData="M256,153m-3,0a3,3 0,1 1,6 0a3,3 0,1 1,-6 0"
- android:fillColor="#ffffff"/>
- <!-- android head and body -->
- <path
- android:pathData="M151,350h199v104h-199z"
- android:fillColor="#5F6368"/>
- <path
- android:pathData="M358.42,350.44C358.36,350.02 358.29,349.6 358.22,349.18C357.8,346.6 357.27,344.04 356.65,341.52C355.57,337.12 354.21,332.82 352.59,328.66C351.22,325.13 349.66,321.7 347.93,318.38C345.7,314.11 343.18,310.01 340.41,306.11C337.01,301.34 333.21,296.86 329.06,292.74C327.32,291.01 325.52,289.34 323.65,287.73C319.62,284.26 315.32,281.09 310.78,278.26C310.82,278.19 310.85,278.12 310.89,278.05C312.97,274.46 315.05,270.88 317.13,267.29C319.17,263.78 321.2,260.28 323.23,256.77C324.69,254.26 326.15,251.74 327.61,249.22C327.95,248.62 328.22,248.01 328.43,247.37C329,245.61 329.02,243.76 328.57,242.03C328.45,241.61 328.31,241.19 328.14,240.78C327.97,240.38 327.77,239.98 327.54,239.6C326.76,238.29 325.65,237.16 324.26,236.33C323.02,235.6 321.64,235.16 320.23,235.03C319.64,234.98 319.04,234.99 318.45,235.05C317.96,235.1 317.47,235.19 316.99,235.32C315.26,235.77 313.67,236.72 312.42,238.08C311.98,238.57 311.58,239.12 311.23,239.71C309.77,242.23 308.31,244.75 306.85,247.27L300.76,257.78C298.68,261.37 296.6,264.96 294.52,268.55C294.29,268.94 294.06,269.33 293.83,269.73C293.52,269.6 293.21,269.48 292.89,269.36C281.43,264.99 269,262.6 256.01,262.6C255.65,262.6 255.3,262.6 254.94,262.6C243.39,262.72 232.29,264.73 221.93,268.33C220.73,268.75 219.55,269.19 218.38,269.65C218.16,269.29 217.95,268.92 217.74,268.55C215.66,264.96 213.58,261.38 211.5,257.79C209.47,254.28 207.43,250.78 205.4,247.27C203.94,244.76 202.48,242.23 201.02,239.72C200.68,239.12 200.28,238.58 199.83,238.09C198.59,236.72 196.99,235.78 195.27,235.32C194.79,235.2 194.3,235.1 193.81,235.05C193.22,234.99 192.62,234.99 192.03,235.04C190.61,235.16 189.23,235.6 188,236.34C186.6,237.16 185.5,238.3 184.71,239.6C184.49,239.99 184.29,240.38 184.12,240.79C183.95,241.2 183.8,241.61 183.69,242.04C183.23,243.76 183.26,245.62 183.82,247.38C184.03,248.01 184.3,248.63 184.65,249.23C186.11,251.74 187.57,254.26 189.02,256.78C191.06,260.28 193.09,263.79 195.12,267.29C197.2,270.88 199.28,274.47 201.36,278.06C201.38,278.09 201.4,278.12 201.41,278.15C197.22,280.76 193.23,283.64 189.47,286.8C187.21,288.69 185.04,290.68 182.96,292.75C178.81,296.87 175.01,301.35 171.6,306.12C168.82,310.02 166.31,314.11 164.09,318.39C162.35,321.71 160.79,325.14 159.42,328.67C157.8,332.83 156.44,337.13 155.36,341.53C154.75,344.05 154.22,346.6 153.79,349.19C153.72,349.61 153.66,350.03 153.59,350.45C153.36,351.95 153.16,353.46 153,354.98L359,354.98C358.84,353.46 358.64,351.95 358.41,350.45L358.42,350.44Z"
- android:fillColor="#5F6368"/>
- </group>
- <!-- stars -->
- <group>
- <path
- android:pathData="M131.04,134.34H127V138.38H131.04V134.34Z"
- android:fillColor="#ffffff"/>
- <path
- android:pathData="M167.04,256H163V260.04H167.04V256Z"
- android:fillColor="#ffffff"/>
- <path
- android:pathData="M373.49,127H369.45V131.04H373.49V127Z"
- android:fillColor="#ffffff"/>
- <path
- android:pathData="M292.04,226H288V230.04H292.04V226Z"
- android:fillColor="#ffffff"/>
- <path
- android:pathData="M319.04,186.91H315V190.95H319.04V186.91Z"
- android:fillColor="#ffffff"/>
- <path
- android:pathData="M355.04,222H351V226.04H355.04V222Z"
- android:fillColor="#ffffff"/>
- <path
- android:pathData="M192.04,136H188V140.04H192.04V136Z"
- android:fillColor="#ffffff"/>
- <path
- android:pathData="M336.08,196H328V204.08H336.08V196Z"
- android:fillColor="#ffffff"/>
- <path
- android:pathData="M222.04,212H218V216.04H222.04V212Z"
- android:fillColor="#ffffff"/>
- <path
- android:pathData="M163.08,175H155V183.08H163.08V175Z"
- android:fillColor="#ffffff"/>
- <path
- android:pathData="M211.08,143H203V151.08H211.08V143Z"
- android:fillColor="#ffffff"/>
- <path
- android:pathData="M369.08,204H361V212.08H369.08V204Z"
- android:fillColor="#ffffff"/>
- <path
- android:pathData="M169.21,204.34H161.13V212.42H169.21V204.34Z"
- android:fillColor="#ffffff"/>
- <path
- android:pathData="M383.04,160.07H374.95V168.15H383.04V160.07Z"
- android:fillColor="#ffffff"/>
- <path
- android:pathData="M192.08,183H184V191.08H192.08V183Z"
- android:fillColor="#ffffff"/>
- </group>
- <!-- patch frame -->
- <path
- android:pathData="M256,446.36C229.63,446.36 212.19,428.06 194.8,397.94C177.41,367.82 91.54,219.08 74.14,188.96C56.75,158.84 49.63,134.59 62.81,111.75C76,88.91 100.56,82.95 135.35,82.95C170.13,82.95 341.87,82.95 376.65,82.95C411.44,82.95 436,88.91 449.19,111.75C462.37,134.59 455.25,158.84 437.86,188.96C420.46,219.08 334.59,367.82 317.2,397.94C299.81,428.06 282.37,446.36 256,446.36H256Z"
+ android:pathData="M127,58.5L385,58.5A68.5,68.5 0,0 1,453.5 127L453.5,385A68.5,68.5 0,0 1,385 453.5L127,453.5A68.5,68.5 0,0 1,58.5 385L58.5,127A68.5,68.5 0,0 1,127 58.5z"
android:strokeWidth="55"
+ android:fillColor="#1D2126"
+ android:strokeColor="#4285F4"/>
+ <path
+ android:pathData="M152.157,72C149.469,72 148.125,72 148.125,72C148.125,72 148.581,70.818 149.493,68.454L157.269,48.222C157.773,46.89 158.025,46.224 158.025,46.224C158.025,46.224 158.913,46.224 160.689,46.224C162.405,46.224 163.263,46.224 163.263,46.224C163.263,46.224 163.521,46.884 164.037,48.204L171.759,68.418C172.683,70.806 173.145,72 173.145,72C173.145,72 171.831,72 169.203,72C167.643,72 166.863,72 166.863,72C166.863,72 166.683,71.448 166.323,70.344L161.481,56.376L160.617,53.496H160.509L159.645,56.358L154.767,70.308C154.419,71.436 154.245,72 154.245,72C154.245,72 153.549,72 152.157,72ZM152.949,66.996L154.479,62.874H166.287L167.871,66.996H152.949ZM186.631,72C184.759,72 183.823,72 183.823,72C183.823,72 183.823,71.148 183.823,69.444V49.59C183.823,47.346 183.823,46.224 183.823,46.224C183.823,46.224 185.065,46.224 187.549,46.224C189.001,46.224 189.727,46.224 189.727,46.224C189.727,46.224 190.021,46.686 190.609,47.61L199.807,62.046H199.915L199.807,58.05V48.708C199.807,47.052 199.807,46.224 199.807,46.224C199.807,46.224 200.737,46.224 202.597,46.224C204.469,46.224 205.405,46.224 205.405,46.224C205.405,46.224 205.405,47.052 205.405,48.708V69.012C205.405,71.004 205.405,72 205.405,72C205.405,72 204.307,72 202.111,72C200.839,72 200.203,72 200.203,72C200.203,72 199.945,71.604 199.429,70.812L189.421,55.53H189.295L189.421,59.508V69.444C189.421,71.148 189.421,72 189.421,72C189.421,72 188.491,72 186.631,72ZM221.683,72C219.559,72 218.497,72 218.497,72C218.497,72 218.497,70.932 218.497,68.796V49.41C218.497,47.286 218.497,46.224 218.497,46.224C218.497,46.224 219.559,46.224 221.683,46.224H227.677C231.613,46.224 234.787,47.406 237.199,49.77C239.611,52.122 240.817,55.23 240.817,59.094C240.817,62.982 239.611,66.108 237.199,68.472C234.787,70.824 231.613,72 227.677,72H221.683ZM224.293,66.852H227.353C229.681,66.876 231.523,66.21 232.879,64.854C234.247,63.486 234.931,61.584 234.931,59.148C234.931,56.736 234.253,54.864 232.897,53.532C231.541,52.2 229.693,51.534 227.353,51.534H224.293V66.852ZM255.401,72C253.469,72 252.503,72 252.503,72C252.503,72 252.503,71.13 252.503,69.39V49.41C252.503,47.286 252.503,46.224 252.503,46.224C252.503,46.224 253.565,46.224 255.689,46.224H262.763C265.319,46.224 267.467,46.98 269.207,48.492C270.959,50.004 271.835,52.02 271.835,54.54C271.835,56.904 271.001,58.848 269.333,60.372C267.677,61.884 265.517,62.64 262.853,62.64H254.915V58.482H262.205C263.285,58.482 264.191,58.146 264.923,57.474C265.667,56.79 266.039,55.896 266.039,54.792C266.039,53.796 265.679,52.962 264.959,52.29C264.251,51.606 263.381,51.264 262.349,51.264H258.299V69.39C258.299,71.13 258.299,72 258.299,72C258.299,72 257.333,72 255.401,72ZM268.703,72C267.551,72 266.975,72 266.975,72C266.975,72 266.759,71.664 266.327,70.992L259.487,60.444L265.607,60.228L270.917,68.004C272.765,70.668 273.689,72 273.689,72C273.689,72 272.027,72 268.703,72ZM295.491,72.594C291.771,72.594 288.603,71.346 285.987,68.85C283.383,66.342 282.081,63.09 282.081,59.094C282.081,55.086 283.389,51.84 286.005,49.356C288.633,46.872 291.789,45.63 295.473,45.63C299.157,45.63 302.313,46.89 304.941,49.41C307.569,51.918 308.883,55.146 308.883,59.094C308.883,63.018 307.575,66.252 304.959,68.796C302.355,71.328 299.199,72.594 295.491,72.594ZM295.473,66.996C297.645,66.996 299.451,66.252 300.891,64.764C302.343,63.264 303.069,61.374 303.069,59.094C303.069,56.79 302.343,54.906 300.891,53.442C299.439,51.966 297.633,51.228 295.473,51.228C293.337,51.228 291.537,51.966 290.073,53.442C288.621,54.918 287.895,56.802 287.895,59.094C287.895,61.386 288.615,63.276 290.055,64.764C291.507,66.252 293.313,66.996 295.473,66.996ZM323.519,72C321.563,72 320.585,72 320.585,72C320.585,72 320.585,71.118 320.585,69.354V48.87C320.585,47.106 320.585,46.224 320.585,46.224C320.585,46.224 321.563,46.224 323.519,46.224C325.475,46.224 326.453,46.224 326.453,46.224C326.453,46.224 326.453,47.106 326.453,48.87V69.354C326.453,71.118 326.453,72 326.453,72C326.453,72 325.475,72 323.519,72ZM342.73,72C340.606,72 339.544,72 339.544,72C339.544,72 339.544,70.932 339.544,68.796V49.41C339.544,47.286 339.544,46.224 339.544,46.224C339.544,46.224 340.606,46.224 342.73,46.224H348.724C352.66,46.224 355.834,47.406 358.246,49.77C360.658,52.122 361.864,55.23 361.864,59.094C361.864,62.982 360.658,66.108 358.246,68.472C355.834,70.824 352.66,72 348.724,72H342.73ZM345.34,66.852H348.4C350.728,66.876 352.57,66.21 353.926,64.854C355.294,63.486 355.978,61.584 355.978,59.148C355.978,56.736 355.3,54.864 353.944,53.532C352.588,52.2 350.74,51.534 348.4,51.534H345.34V66.852Z"
+ android:fillColor="#ffffff"/>
+ <path
+ android:pathData="M196.971,198.17H257.84L196.971,259.04V198.17Z"
+ android:fillColor="#1F8E3D"/>
+ <path
+ android:pathData="M318.709,319.909L257.84,319.909L318.709,259.04L318.709,319.909Z"
+ android:fillColor="#1F8E3D"/>
+ <path
+ android:pathData="M318.709,198.17L318.709,259.04L257.84,198.17L318.709,198.17Z"
+ android:fillColor="#34A853"/>
+ <path
+ android:pathData="M196.971,319.909L196.971,259.04L257.84,319.909L196.971,319.909Z"
+ android:fillColor="#34A853"/>
+ <path
+ android:pathData="M257.84,259.04L196.971,259.04L257.84,198.171L257.84,259.04Z"
+ android:fillColor="#34A853"/>
+ <path
+ android:pathData="M257.84,198.17L196.971,198.17L257.84,137.301L257.84,198.17Z"
+ android:fillColor="#34A853"/>
+ <path
+ android:pathData="M257.84,319.909L318.709,319.909L257.84,380.778L257.84,319.909Z"
+ android:fillColor="#34A853"/>
+ <path
+ android:pathData="M257.84,259.04L318.709,259.04L257.84,319.91L257.84,259.04Z"
+ android:fillColor="#34A853"/>
+ <path
+ android:pathData="M318.709,259.04L379.579,259.04L318.709,319.91L318.709,259.04Z"
+ android:fillColor="#34A853"/>
+ <path
+ android:pathData="M196.971,259.04L136.101,259.04L196.971,198.171L196.971,259.04Z"
+ android:fillColor="#34A853"/>
+ <path
+ android:pathData="M257.84,259.04L257.84,198.171L318.709,259.04L257.84,259.04Z"
+ android:fillColor="#1F8E3D"/>
+ <path
+ android:pathData="M257.84,198.17L257.84,137.301L318.709,198.17L257.84,198.17Z"
+ android:fillColor="#1F8E3D"/>
+ <path
+ android:pathData="M257.84,319.909L257.84,380.778L196.971,319.909L257.84,319.909Z"
+ android:fillColor="#1F8E3D"/>
+ <path
+ android:pathData="M318.709,259.04L318.709,198.171L379.579,259.04L318.709,259.04Z"
+ android:fillColor="#1F8E3D"/>
+ <path
+ android:pathData="M196.971,259.04L196.971,319.91L136.101,259.04L196.971,259.04Z"
+ android:fillColor="#1F8E3D"/>
+ <path
+ android:pathData="M257.84,259.04L257.84,319.91L196.971,259.04L257.84,259.04Z"
+ android:fillColor="#1F8E3D"/>
+ <path
+ android:pathData="M342.409,405.074C340.766,405.074 339.348,404.477 338.153,403.282C336.989,402.088 336.406,400.654 336.406,398.982V355.485L328.567,361.129C327.462,361.935 326.222,362.219 324.849,361.98C323.475,361.741 322.385,361.054 321.579,359.919C320.772,358.785 320.488,357.53 320.727,356.157C320.996,354.753 321.683,353.633 322.788,352.797L339.497,340.881C339.915,340.582 340.363,340.314 340.841,340.075C341.319,339.806 341.961,339.671 342.767,339.671C344.35,339.671 345.679,340.239 346.754,341.374C347.859,342.479 348.412,343.852 348.412,345.495V398.982C348.412,400.654 347.814,402.088 346.62,403.282C345.455,404.477 344.051,405.074 342.409,405.074ZM382.377,405.522C375.449,405.522 369.804,403.447 365.444,399.296C361.114,395.144 358.949,389.873 358.949,383.482C358.949,379.122 359.919,375.046 361.86,371.253C363.831,367.46 366.534,363.339 369.969,358.889L382.646,342.538C383.572,341.344 384.766,340.627 386.23,340.388C387.723,340.149 389.082,340.448 390.306,341.284C391.68,342.27 392.486,343.554 392.725,345.137C392.964,346.719 392.546,348.153 391.471,349.437L383.452,359.651C382.228,361.174 379.719,363.309 375.926,366.057C372.164,368.804 369.595,374.553 368.221,383.303L361.726,383.258C362.652,376.21 365.504,371.029 370.282,367.714C375.09,364.399 380.272,362.742 385.826,362.742C391.322,362.742 396.01,364.653 399.893,368.476C403.775,372.268 405.716,377.211 405.716,383.303C405.716,389.724 403.521,395.04 399.131,399.251C394.771,403.432 389.186,405.522 382.377,405.522ZM382.332,395.04C385.826,395.04 388.693,393.965 390.933,391.815C393.203,389.664 394.338,386.902 394.338,383.527C394.338,380.212 393.203,377.48 390.933,375.329C388.693,373.149 385.856,372.059 382.422,372.059C378.958,372.059 376.076,373.149 373.776,375.329C371.507,377.48 370.372,380.227 370.372,383.572C370.372,386.887 371.492,389.634 373.731,391.815C376.001,393.965 378.868,395.04 382.332,395.04Z"
+ android:fillColor="#E8F5E9"/>
+ <path
+ android:pathData="M265.087,135.953L380.927,251.793A10.249,10.249 90,0 1,380.927 266.287L265.087,382.126A10.249,10.249 90,0 1,250.593 382.126L134.753,266.287A10.249,10.249 0,0 1,134.753 251.793L250.593,135.953A10.249,10.249 90,0 1,265.087 135.953z"
+ android:strokeWidth="14.3349"
android:fillColor="#00000000"
- android:strokeColor="#34A853"/>
- <!-- text: ANDROID -->
+ android:strokeColor="#5F6368"/>
<path
- android:pathData="M170.11,92.71C170.97,94.9 171.41,96 171.41,96C171.41,96 170.37,96 168.29,96C166.22,96 165.18,96 165.18,96C165.18,96 164.93,95.27 164.42,93.82L159.71,80.63L158.82,77.75H158.61L157.82,80.63L153.28,93.82C152.82,95.27 152.6,96 152.6,96C152.6,96 151.54,96 149.43,96C147.33,96 146.28,96 146.28,96C146.28,96 146.7,94.89 147.56,92.67L155.21,72.87C155.89,71.07 156.23,70.17 156.23,70.17C156.23,70.17 157.06,70.17 158.72,70.17C160.52,70.17 161.42,70.17 161.42,70.17C161.42,70.17 161.76,71.07 162.46,72.87L170.11,92.71ZM166.04,91.12H158.64V86.91H164.42L166.04,91.12ZM158.64,91.12H151.08L152.63,86.91H158.64V91.12ZM203.85,93.46C203.85,95.15 203.85,96 203.85,96C203.85,96 202.95,96 201.15,96C199.38,96 198.5,96 198.5,96C198.5,96 198.03,95.26 197.08,93.79L188.08,79.75H187.88L188.01,83.45V93.25C188.01,95.08 188.01,96 188.01,96C188.01,96 187.03,96 185.09,96C183.15,96 182.17,96 182.17,96C182.17,96 182.17,95.08 182.17,93.25L182.16,73.9C182.16,71.45 182.16,70.22 182.16,70.22C182.16,70.22 183.19,70.22 185.25,70.22C187.24,70.22 188.24,70.22 188.24,70.22C188.24,70.22 188.7,70.96 189.63,72.42L198.16,85.85H198.36L198.21,82.09V72.89C198.21,71.11 198.21,70.22 198.21,70.22C198.21,70.22 199.15,70.22 201.04,70.22C202.91,70.22 203.85,70.22 203.85,70.22C203.85,70.22 203.85,71.11 203.85,72.89V93.46ZM226.52,96H220.17C218.24,96 217.27,96 217.27,96C217.27,96 217.27,95.02 217.27,93.05V73.19C217.27,71.21 217.27,70.22 217.27,70.22C217.27,70.22 218.24,70.22 220.17,70.22H226.52C230.46,70.22 233.63,71.41 236.03,73.77C238.43,76.12 239.63,79.23 239.63,83.09C239.63,86.98 238.43,90.11 236.03,92.47C233.63,94.82 230.46,96 226.52,96ZM223.17,75.64V90.74H226.18C228.46,90.77 230.27,90.11 231.62,88.78C232.96,87.44 233.63,85.57 233.63,83.17C233.63,80.78 232.96,78.93 231.62,77.62C230.28,76.3 228.47,75.64 226.18,75.64H223.17ZM257.51,93.23C257.51,95.08 257.51,96 257.51,96C257.51,96 256.54,96 254.6,96C252.66,96 251.7,96 251.7,96C251.7,96 251.7,95.09 251.7,93.26V73.19C251.7,71.21 251.7,70.22 251.7,70.22C251.7,70.22 252.66,70.22 254.6,70.22H261.89C264.44,70.22 266.6,70.98 268.35,72.49C270.1,74 270.98,76.03 270.98,78.56C270.98,80.9 270.14,82.83 268.47,84.35C266.81,85.87 264.65,86.62 262.01,86.62H254.02V82.41H261.33C262.4,82.41 263.29,82.08 264.01,81.42C264.73,80.75 265.09,79.88 265.09,78.81C265.09,77.84 264.74,77.03 264.05,76.38C263.35,75.72 262.49,75.39 261.47,75.39H257.51V93.23ZM264.77,93.82L258.66,84.46L264.8,84.25L271.23,93.62C272.37,95.21 272.94,96 272.94,96C272.94,96 271.82,96 269.57,96C267.3,96 266.17,96 266.17,96C266.17,96 265.7,95.27 264.77,93.82ZM296.04,96.58C292.33,96.58 289.16,95.33 286.52,92.85C283.89,90.37 282.58,87.11 282.58,83.09C282.58,79.07 283.9,75.83 286.54,73.36C289.19,70.88 292.36,69.65 296.04,69.65C299.71,69.65 302.87,70.9 305.51,73.41C308.16,75.91 309.49,79.13 309.49,83.09C309.49,87.03 308.17,90.26 305.53,92.8C302.9,95.32 299.74,96.58 296.04,96.58ZM296.04,90.83C298.19,90.83 299.98,90.1 301.41,88.64C302.85,87.17 303.57,85.33 303.57,83.09C303.57,80.84 302.84,78.99 301.39,77.55C299.95,76.11 298.17,75.39 296.04,75.39C293.92,75.39 292.13,76.12 290.68,77.57C289.24,79.01 288.52,80.85 288.52,83.09C288.52,85.35 289.23,87.2 290.66,88.66C292.1,90.11 293.89,90.83 296.04,90.83ZM327.64,93.05C327.64,95.02 327.64,96 327.64,96C327.64,96 326.63,96 324.61,96C322.59,96 321.57,96 321.57,96C321.57,96 321.57,95.02 321.57,93.05V73.18C321.57,71.21 321.57,70.22 321.57,70.22C321.57,70.22 322.58,70.22 324.6,70.22C326.63,70.22 327.64,70.22 327.64,70.22C327.64,70.22 327.64,71.21 327.64,73.18V93.05ZM350.31,96H343.96C342.03,96 341.06,96 341.06,96C341.06,96 341.06,95.02 341.06,93.05V73.19C341.06,71.21 341.06,70.22 341.06,70.22C341.06,70.22 342.03,70.22 343.96,70.22H350.31C354.25,70.22 357.42,71.41 359.82,73.77C362.22,76.12 363.42,79.23 363.42,83.09C363.42,86.98 362.22,90.11 359.82,92.47C357.42,94.82 354.25,96 350.31,96ZM346.96,75.64V90.74H349.97C352.25,90.77 354.06,90.11 355.41,88.78C356.75,87.44 357.42,85.57 357.42,83.17C357.42,80.78 356.75,78.93 355.41,77.62C354.07,76.3 352.26,75.64 349.97,75.64H346.96Z"
- android:fillColor="#E9F3EB"/>
- <!-- text: 15 -->
+ android:pathData="M172.353,318.422C172.818,317.837 172.778,316.987 172.249,316.459V316.459C171.631,315.841 170.606,315.905 170.06,316.587C133.391,362.417 114.184,398.637 123.921,408.374C138.509,422.962 212.535,372.586 289.264,295.857C296.618,288.503 303.729,281.175 310.565,273.921C311.305,273.135 311.286,271.903 310.522,271.139L306.337,266.954C305.538,266.156 304.238,266.176 303.463,266.998C296.672,274.202 289.587,281.503 282.248,288.842C212.272,358.818 145.748,405.747 133.662,393.66C126.026,386.024 141.946,356.659 172.353,318.422Z"
+ android:fillColor="#C6FF00"
+ android:fillType="evenOdd"/>
<path
- android:pathData="M236.59,363.25C236.59,365.75 236.59,367 236.59,367C236.59,367 235.32,367 232.79,367C230.32,367 229.09,367 229.09,367C229.09,367 229.09,365.75 229.09,363.25V305.85L216.64,314.75C215,315.92 214.19,316.5 214.19,316.5C214.19,316.5 213.54,315.5 212.24,313.5C210.97,311.6 210.34,310.65 210.34,310.65C210.34,310.62 211.2,309.98 212.94,308.75L227.64,298.2C230.3,296.23 231.64,295.25 231.64,295.25C231.64,295.25 232.1,295.25 233.04,295.25C235.4,295.25 236.59,295.25 236.59,295.25C236.59,295.25 236.59,296.47 236.59,298.9V363.25ZM247.09,330L251.19,299C251.52,296.6 251.69,295.4 251.69,295.4C251.69,295.4 252.77,295.4 254.94,295.4H284.54C286.97,295.4 288.19,295.4 288.19,295.4C288.19,295.4 288.19,296.58 288.19,298.95C288.19,301.48 288.19,302.75 288.19,302.75C288.19,302.75 286.97,302.75 284.54,302.75H257.49L254.19,327.45L254.39,327.5C256.09,325.77 258.27,324.38 260.94,323.35C263.61,322.28 266.65,321.75 270.09,321.75C276.55,321.75 281.99,323.97 286.39,328.4C290.79,332.8 292.99,338.32 292.99,344.95C292.99,351.75 290.8,357.4 286.44,361.9C282.11,366.37 276.42,368.6 269.39,368.6C263.09,368.6 257.77,367 253.44,363.8C249.1,360.6 246.26,356.85 244.89,352.55C244.26,350.32 243.94,349.2 243.94,349.2C243.94,349.2 245.09,348.77 247.39,347.9C249.79,347 250.99,346.55 250.99,346.55C250.99,346.55 251.3,347.73 251.94,350.1C252.8,352.73 254.71,355.27 257.64,357.7C260.61,360.13 264.44,361.35 269.14,361.35C274.27,361.35 278.24,359.88 281.04,356.95C283.84,353.98 285.24,350.03 285.24,345.1C285.24,340.37 283.67,336.52 280.54,333.55C277.44,330.58 273.4,329.1 268.44,329.1C265.47,329.1 262.95,329.52 260.89,330.35C258.82,331.15 257.09,332.28 255.69,333.75C254.39,335.25 253.74,336 253.74,336C253.74,336 252.55,335.52 250.19,334.55C247.85,333.62 246.69,333.15 246.69,333.15C246.69,333.15 246.82,332.1 247.09,330Z"
- android:fillColor="#E9F3EB"/>
- <!-- spacecraft -->
+ android:pathData="M347.267,248.089C348.867,247.66 350.512,248.61 350.941,250.21C351.37,251.81 350.42,253.455 348.82,253.884L347.267,248.089ZM348.82,253.884L321.452,261.217L319.899,255.422L347.267,248.089L348.82,253.884Z"
+ android:fillColor="#000000"/>
<path
- android:pathData="M256.12,121C249.43,121 244,126.27 244,132.77V147.29C244,148.54 245.02,149.56 246.27,149.56C247.53,149.56 248.55,148.55 248.55,147.29V143.38C248.55,140.87 250.58,138.83 253.09,138.83H259.15C261.66,138.83 263.7,140.87 263.7,143.38V147.29C263.7,148.54 264.71,149.56 265.97,149.56C267.23,149.56 268.24,148.55 268.24,147.29V132.77C268.24,126.27 262.82,121 256.12,121H256.12Z"
- android:fillColor="#E9F3EB"/>
+ android:pathData="M325.111,230.175C325.54,228.575 327.185,227.625 328.785,228.054C330.386,228.483 331.335,230.128 330.907,231.728L325.111,230.175ZM330.907,231.728L323.573,259.096L317.778,257.543L325.111,230.175L330.907,231.728Z"
+ android:fillColor="#000000"/>
+ <path
+ android:pathData="M335.665,242.984C327.494,234.813 314.434,234.623 306.498,242.559L288.774,260.283C287.243,261.814 287.243,264.3 288.774,265.831C290.306,267.363 292.791,267.363 294.323,265.831L299.1,261.054C302.165,257.989 307.132,257.989 310.197,261.054L317.595,268.452C320.66,271.517 320.66,276.484 317.595,279.549L312.818,284.326C311.286,285.858 311.286,288.344 312.818,289.875C314.349,291.406 316.835,291.406 318.366,289.875L336.09,272.151C344.026,264.215 343.836,251.155 335.665,242.984Z"
+ android:fillColor="#000000"/>
+ <path
+ android:pathData="M123.071,117.004l7.071,7.071l-7.071,7.071l-7.071,-7.071z"
+ android:fillColor="#E8F5E9"/>
+ <path
+ android:pathData="M108.071,332l7.071,7.071l-7.071,7.071l-7.071,-7.071z"
+ android:fillColor="#E8F5E9"/>
+ <path
+ android:pathData="M120.607,299l10.607,10.607l-10.607,10.607l-10.607,-10.607z"
+ android:fillColor="#E8F5E9"/>
+ <path
+ android:pathData="M123.071,183.435l7.071,7.071l-7.071,7.071l-7.071,-7.071z"
+ android:fillColor="#E8F5E9"/>
+ <path
+ android:pathData="M174.606,137l10.607,10.607l-10.607,10.607l-10.607,-10.607z"
+ android:fillColor="#E8F5E9"/>
+ <path
+ android:pathData="M145.214,139.149l7.071,7.071l-7.071,7.071l-7.071,-7.071z"
+ android:fillColor="#E8F5E9"/>
+ <path
+ android:pathData="M215.607,385l10.607,10.607l-10.607,10.607l-10.607,-10.607z"
+ android:fillColor="#E8F5E9"/>
+ <path
+ android:pathData="M227.071,110l7.071,7.071l-7.071,7.071l-7.071,-7.071z"
+ android:fillColor="#E8F5E9"/>
+ <path
+ android:pathData="M400.569,124.075l-7.071,7.071l-7.071,-7.071l7.071,-7.071z"
+ android:fillColor="#E8F5E9"/>
+ <path
+ android:pathData="M388.213,149.606l-10.607,10.607l-10.607,-10.607l10.607,-10.607z"
+ android:fillColor="#E8F5E9"/>
+ <path
+ android:pathData="M331.142,121.071l-7.071,7.071l-7.071,-7.071l7.071,-7.071z"
+ android:fillColor="#E8F5E9"/>
+ <path
+ android:pathData="M338.142,153.071l-7.071,7.071l-7.071,-7.071l7.071,-7.071z"
+ android:fillColor="#E8F5E9"/>
+ <path
+ android:pathData="M374.142,320.071l-7.071,7.071l-7.071,-7.071l7.071,-7.071z"
+ android:fillColor="#E8F5E9"/>
+ <path
+ android:pathData="M307.213,109.607l-10.607,10.607l-10.607,-10.607l10.607,-10.607z"
+ android:fillColor="#E8F5E9"/>
+ <path
+ android:pathData="M181.142,353.071l-7.071,7.071l-7.071,-7.071l7.071,-7.071z"
+ android:fillColor="#E8F5E9"/>
+ <path
+ android:pathData="M400.573,190.501l-10.607,10.607l-10.607,-10.607l10.607,-10.607z"
+ android:fillColor="#E8F5E9"/>
</vector>
diff --git a/core/res/res/drawable-nodpi/stat_sys_adb.xml b/core/res/res/drawable-nodpi/stat_sys_adb.xml
index 6ce4b9d..973010e 100644
--- a/core/res/res/drawable-nodpi/stat_sys_adb.xml
+++ b/core/res/res/drawable-nodpi/stat_sys_adb.xml
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2024 The Android Open Source Project
@@ -13,7 +14,6 @@
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"
@@ -21,104 +21,35 @@
android:viewportHeight="24">
<group>
<clip-path
- android:pathData="
- M12.6495 17.375
- C12.3608 17.875 11.6392 17.875 11.3505 17.375
- L3.98927 4.625
- C3.70059 4.125 4.06143 3.5 4.63878 3.5
- L19.3612 3.5
- C19.9386 3.5 20.2994 4.125 20.0107 4.625
- L12.6495 17.375
- Z
- "/>
+ android:pathData="M0,0h24v24h-24z"/>
<path
- android:pathData="M5,12 h14v9.8h-14z"
- android:fillColor="#ffffff"/>
+ android:pathData="M12,8V12H8L12,8Z"
+ android:fillColor="#D9D9D9"/>
<path
- android:pathData="
- M15.97 10.48
- C15.97 10.46 15.97 10.45 15.96 10.43
- C15.95 10.33 15.93 10.23 15.90 10.13
- C15.86 9.96 15.81 9.79 15.75 9.63
- C15.69 9.50 15.63 9.36 15.57 9.23
- C15.48 9.07 15.38 8.91 15.27 8.76
- C15.14 8.57 14.99 8.40 14.83 8.24
- C14.76 8.17 14.69 8.11 14.62 8.04
- C14.47 7.91 14.30 7.78 14.12 7.67
- C14.12 7.67 14.13 7.67 14.13 7.67
- C14.21 7.53 14.29 7.39 14.37 7.25
- C14.45 7.11 14.53 6.98 14.61 6.84
- C14.66 6.74 14.72 6.64 14.78 6.55
- C14.79 6.52 14.80 6.50 14.81 6.48
- C14.83 6.41 14.83 6.33 14.81 6.27
- C14.81 6.25 14.80 6.24 14.80 6.22
- C14.79 6.20 14.78 6.19 14.77 6.17
- C14.74 6.12 14.70 6.08 14.65 6.05
- C14.60 6.02 14.54 6 14.49 6
- C14.47 5.99 14.44 5.99 14.42 6
- C14.40 6 14.38 6 14.36 6.01
- C14.30 6.02 14.23 6.06 14.19 6.11
- C14.17 6.13 14.15 6.15 14.14 6.18
- C14.08 6.28 14.03 6.37 13.97 6.47
- L13.73 6.88
- C13.65 7.02 13.57 7.16 13.49 7.30
- C13.48 7.31 13.47 7.33 13.46 7.34
- C13.45 7.34 13.44 7.33 13.43 7.33
- C12.98 7.16 12.50 7.07 12 7.07
- C11.98 7.07 11.97 7.07 11.95 7.07
- C11.51 7.07 11.07 7.15 10.67 7.29
- C10.63 7.31 10.58 7.32 10.53 7.34
- C10.53 7.33 10.52 7.31 10.51 7.30
- C10.43 7.16 10.35 7.02 10.27 6.88
- C10.19 6.74 10.11 6.61 10.03 6.47
- C9.97 6.37 9.92 6.28 9.86 6.18
- C9.85 6.15 9.83 6.13 9.81 6.11
- C9.77 6.06 9.70 6.03 9.64 6.01
- C9.62 6 9.60 6 9.58 6
- C9.56 5.99 9.53 5.99 9.51 6
- C9.46 6 9.40 6.02 9.35 6.05
- C9.30 6.08 9.26 6.12 9.23 6.17
- C9.22 6.19 9.21 6.20 9.20 6.22
- C9.20 6.24 9.19 6.25 9.19 6.27
- C9.17 6.34 9.17 6.41 9.19 6.48
- C9.20 6.50 9.21 6.52 9.22 6.55
- C9.28 6.65 9.34 6.74 9.39 6.84
- C9.47 6.98 9.55 7.11 9.63 7.25
- C9.71 7.39 9.79 7.53 9.87 7.67
- C9.87 7.67 9.87 7.67 9.88 7.67
- C9.71 7.77 9.56 7.88 9.41 8.01
- C9.32 8.08 9.24 8.16 9.16 8.24
- C9 8.40 8.85 8.57 8.72 8.76
- C8.61 8.91 8.51 9.07 8.43 9.23
- C8.36 9.36 8.30 9.50 8.24 9.63
- C8.18 9.79 8.13 9.96 8.09 10.13
- C8.06 10.23 8.04 10.33 8.03 10.43
- C8.02 10.45 8.02 10.46 8.02 10.48
- C8.01 10.54 8 10.60 8 10.65
- L16 10.65
- C15.99 10.60 15.98 10.54 15.97 10.48
- L15.97 10.48
- Z
- "
- android:fillColor="#ffffff"/>
+ android:pathData="M12,4V8H8L12,4Z"
+ android:fillColor="#D9D9D9"/>
+ <path
+ android:pathData="M8,8V12H4L8,8Z"
+ android:fillColor="#D9D9D9"/>
+ <path
+ android:pathData="M12,16L12,12L16,12L12,16Z"
+ android:fillColor="#D9D9D9"/>
+ <path
+ android:pathData="M12,20L12,16L16,16L12,20Z"
+ android:fillColor="#D9D9D9"/>
+ <path
+ android:pathData="M16,16L16,12L20,12L16,16Z"
+ android:fillColor="#D9D9D9"/>
+ <path
+ android:pathData="M12,8L16,8L16,12L12,8Z"
+ android:fillColor="#D9D9D9"/>
+ <path
+ android:pathData="M12,16L8,16L8,12L12,16Z"
+ android:fillColor="#D9D9D9"/>
+ <path
+ android:pathData="M13.096,2.0354L21.9646,10.904A1.55,1.55 90,0 1,21.9646 13.096L13.096,21.9646A1.55,1.55 90,0 1,10.904 21.9646L2.0354,13.096A1.55,1.55 0,0 1,2.0354 10.904L10.904,2.0354A1.55,1.55 0,0 1,13.096 2.0354z"
+ android:strokeWidth="1.5"
+ android:fillColor="#00000000"
+ android:strokeColor="#FF00FF"/>
</group>
- <path
- android:pathData="
- M12,20.923
- C10.764,20.923 9.946,20.065 9.131,18.653
- C8.316,17.241 4.291,10.269 3.476,8.857
- C2.66,7.445 2.326,6.309 2.944,5.238
- C3.563,4.167 4.714,3.888 6.344,3.888
- C7.975,3.888 16.025,3.888 17.656,3.888
- C19.286,3.888 20.437,4.167 21.056,5.238
- C21.674,6.309 21.34,7.445 20.524,8.857
- C19.709,10.269 15.684,17.241 14.869,18.653
- C14.054,20.065 13.236,20.923 12,20.923
- H12
- Z
- "
- android:strokeWidth="2"
- android:fillColor="#00000000"
- android:strokeColor="#ffffff"/>
</vector>
-
diff --git a/core/tests/coretests/src/android/app/NotificationManagerTest.java b/core/tests/coretests/src/android/app/NotificationManagerTest.java
index a47fc94..6538ce8 100644
--- a/core/tests/coretests/src/android/app/NotificationManagerTest.java
+++ b/core/tests/coretests/src/android/app/NotificationManagerTest.java
@@ -18,9 +18,11 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.atMost;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -104,6 +106,22 @@
@Test
@EnableFlags(Flags.FLAG_NM_BINDER_PERF_THROTTLE_NOTIFY)
+ public void notifyAsPackage_rapidUpdate_isThrottled() throws Exception {
+ Notification n = exampleNotification();
+
+ for (int i = 0; i < 100; i++) {
+ mNotificationManager.notifyAsPackage("some.package.name", "tag", 1, n);
+ mClock.advanceByMillis(5);
+ }
+
+ verify(mNotificationManager.mBackendService, atLeast(20)).enqueueNotificationWithTag(
+ eq("some.package.name"), any(), any(), anyInt(), any(), anyInt());
+ verify(mNotificationManager.mBackendService, atMost(30)).enqueueNotificationWithTag(
+ eq("some.package.name"), any(), any(), anyInt(), any(), anyInt());
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_NM_BINDER_PERF_THROTTLE_NOTIFY)
public void cancel_unnecessaryAndRapid_isThrottled() throws Exception {
for (int i = 0; i < 100; i++) {
@@ -165,6 +183,66 @@
any(), any(), anyInt(), anyInt());
}
+ @Test
+ @EnableFlags(Flags.FLAG_NM_BINDER_PERF_THROTTLE_NOTIFY)
+ public void enqueue_afterCancel_isNotUpdateAndIsNotThrottled() throws Exception {
+ // First, hit the enqueue threshold.
+ Notification n = exampleNotification();
+ for (int i = 0; i < 100; i++) {
+ mNotificationManager.notify(1, n);
+ mClock.advanceByMillis(1);
+ }
+ verify(mNotificationManager.mBackendService, atMost(30)).enqueueNotificationWithTag(any(),
+ any(), any(), anyInt(), any(), anyInt());
+ reset(mNotificationManager.mBackendService);
+
+ // Now cancel that notification and then post it again. That should work.
+ mNotificationManager.cancel(1);
+ mNotificationManager.notify(1, n);
+ verify(mNotificationManager.mBackendService).enqueueNotificationWithTag(any(), any(), any(),
+ anyInt(), any(), anyInt());
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_NM_BINDER_PERF_THROTTLE_NOTIFY)
+ public void enqueue_afterCancelAsPackage_isNotUpdateAndIsNotThrottled() throws Exception {
+ // First, hit the enqueue threshold.
+ Notification n = exampleNotification();
+ for (int i = 0; i < 100; i++) {
+ mNotificationManager.notify(1, n);
+ mClock.advanceByMillis(1);
+ }
+ verify(mNotificationManager.mBackendService, atMost(30)).enqueueNotificationWithTag(any(),
+ any(), any(), anyInt(), any(), anyInt());
+ reset(mNotificationManager.mBackendService);
+
+ // Now cancel that notification and then post it again. That should work.
+ mNotificationManager.cancelAsPackage(mContext.getPackageName(), /* tag= */ null, 1);
+ mNotificationManager.notify(1, n);
+ verify(mNotificationManager.mBackendService).enqueueNotificationWithTag(any(), any(), any(),
+ anyInt(), any(), anyInt());
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_NM_BINDER_PERF_THROTTLE_NOTIFY)
+ public void enqueue_afterCancelAll_isNotUpdateAndIsNotThrottled() throws Exception {
+ // First, hit the enqueue threshold.
+ Notification n = exampleNotification();
+ for (int i = 0; i < 100; i++) {
+ mNotificationManager.notify(1, n);
+ mClock.advanceByMillis(1);
+ }
+ verify(mNotificationManager.mBackendService, atMost(30)).enqueueNotificationWithTag(any(),
+ any(), any(), anyInt(), any(), anyInt());
+ reset(mNotificationManager.mBackendService);
+
+ // Now cancel all notifications and then post it again. That should work.
+ mNotificationManager.cancelAll();
+ mNotificationManager.notify(1, n);
+ verify(mNotificationManager.mBackendService).enqueueNotificationWithTag(any(), any(), any(),
+ anyInt(), any(), anyInt());
+ }
+
private Notification exampleNotification() {
return new Notification.Builder(mContext, "channel")
.setSmallIcon(android.R.drawable.star_big_on)
diff --git a/graphics/java/android/graphics/BlendModeColorFilter.java b/graphics/java/android/graphics/BlendModeColorFilter.java
index 7caeb42..d4e2373 100644
--- a/graphics/java/android/graphics/BlendModeColorFilter.java
+++ b/graphics/java/android/graphics/BlendModeColorFilter.java
@@ -71,7 +71,7 @@
return false;
}
final BlendModeColorFilter other = (BlendModeColorFilter) object;
- return other.mMode == mMode;
+ return (other.mMode == mMode && other.mColor == mColor);
}
@Override
diff --git a/libs/WindowManager/Shell/multivalentTests/AndroidManifest.xml b/libs/WindowManager/Shell/multivalentTests/AndroidManifest.xml
index f8f8338..fd578a9 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 ffcd7d4..bb111db 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 2b4e541..c62d2a0 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.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 @@
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 680d015..3043e2b 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.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 @@
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 3e01256..957f0ca 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 @@
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 @@
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 @@
mainExecutor = TestShellExecutor()
bgExecutor = TestShellExecutor()
- container = FrameLayout(context)
-
animationHelper = BubbleBarAnimationHelper(context, bubblePositioner)
}
@@ -121,7 +127,7 @@
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 @@
.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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
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 5c86b32..9b1645e 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.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.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 @@
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 9aba3aa..8efeecb 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.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.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 @@
@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 @@
BubbleDataRepository dataRepository,
@Nullable IStatusBarService statusBarService,
WindowManager windowManager,
- WindowManagerShellWrapper windowManagerShellWrapper,
+ DisplayInsetsController displayInsetsController,
+ DisplayImeController displayImeController,
UserManager userManager,
LauncherApps launcherApps,
BubbleLogger bubbleLogger,
@@ -318,7 +321,8 @@
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 @@
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);
@@ -1441,6 +1449,15 @@
}
/**
+ * Expands and selects a bubble created from a running task in a different mode.
+ *
+ * @param taskInfo the task.
+ */
+ public void expandStackAndSelectBubble(ActivityManager.RunningTaskInfo taskInfo) {
+ // TODO(384976265): Not implemented yet
+ }
+
+ /**
* Expands and selects a bubble based on the provided {@link BubbleEntry}. If no bubble
* exists for this entry, and it is able to bubble, a new bubble will be created.
*
@@ -2515,16 +2532,57 @@
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 de85d9a..1a61793 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 @@
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 @@
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 3188e5b..de6d1f6 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.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 @@
return animator;
}
-
/**
* Animate the expanded bubble when it is being dragged
*/
@@ -586,6 +587,18 @@
}
}
+ /** 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 65c929a..e073b02 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 @@
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 @@
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 @@
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 @@
}
}
+ /** 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 88f34f3..eaa0bd2 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 @@
}
}
+ /** 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 9ebb7f5..eb1e727 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 @@
// 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 @@
* 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 @@
/** @hide */
@IntDef(prefix = {"IME_ANIMATION_"}, value = {
+ IME_ANIMATION_DEFAULT,
IME_ANIMATION_NO_ALPHA,
})
@interface ImeAnimationFlags {
@@ -758,7 +767,7 @@
@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 a34d7be..8851b6a 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 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 @@
// 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 @@
}
}
- 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/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index eab7f6c..1408b6e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -124,6 +124,7 @@
import com.android.wm.shell.sysui.ShellInterface;
import com.android.wm.shell.taskview.TaskViewFactory;
import com.android.wm.shell.taskview.TaskViewFactoryController;
+import com.android.wm.shell.taskview.TaskViewRepository;
import com.android.wm.shell.taskview.TaskViewTransitions;
import com.android.wm.shell.transition.FocusTransitionObserver;
import com.android.wm.shell.transition.HomeTransitionObserver;
@@ -772,8 +773,15 @@
@WMSingleton
@Provides
- static TaskViewTransitions provideTaskViewTransitions(Transitions transitions) {
- return new TaskViewTransitions(transitions);
+ static TaskViewTransitions provideTaskViewTransitions(Transitions transitions,
+ TaskViewRepository repository) {
+ return new TaskViewTransitions(transitions, repository);
+ }
+
+ @WMSingleton
+ @Provides
+ static TaskViewRepository provideTaskViewRepository() {
+ return new TaskViewRepository();
}
// Workaround for dynamic overriding with a default implementation, see {@link DynamicOverride}
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 d02c6b0..48b0a6c 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.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 @@
FloatingContentCoordinator floatingContentCoordinator,
IStatusBarService statusBarService,
WindowManager windowManager,
- WindowManagerShellWrapper windowManagerShellWrapper,
+ DisplayInsetsController displayInsetsController,
+ DisplayImeController displayImeController,
UserManager userManager,
LauncherApps launcherApps,
TaskStackListenerImpl taskStackListener,
@@ -265,7 +265,8 @@
new BubblePersistentRepository(context)),
statusBarService,
windowManager,
- windowManagerShellWrapper,
+ displayInsetsController,
+ displayImeController,
userManager,
launcherApps,
logger,
@@ -735,7 +736,8 @@
DesktopModeEventLogger desktopModeEventLogger,
DesktopModeUiEventLogger desktopModeUiEventLogger,
DesktopTilingDecorViewModel desktopTilingDecorViewModel,
- DesktopWallpaperActivityTokenProvider desktopWallpaperActivityTokenProvider) {
+ DesktopWallpaperActivityTokenProvider desktopWallpaperActivityTokenProvider,
+ Optional<BubbleController> bubbleController) {
return new DesktopTasksController(
context,
shellInit,
@@ -767,7 +769,8 @@
desktopModeEventLogger,
desktopModeUiEventLogger,
desktopTilingDecorViewModel,
- desktopWallpaperActivityTokenProvider);
+ desktopWallpaperActivityTokenProvider,
+ bubbleController);
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 4e7cba2..d180ea7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -73,6 +73,7 @@
import com.android.wm.shell.R
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.bubbles.BubbleController
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayLayout
import com.android.wm.shell.common.ExternalInterfaceBinder
@@ -172,6 +173,7 @@
private val desktopModeUiEventLogger: DesktopModeUiEventLogger,
private val desktopTilingDecorViewModel: DesktopTilingDecorViewModel,
private val desktopWallpaperActivityTokenProvider: DesktopWallpaperActivityTokenProvider,
+ private val bubbleController: Optional<BubbleController>,
) :
RemoteCallable<DesktopTasksController>,
Transitions.TransitionHandler,
@@ -2190,6 +2192,19 @@
}
}
+ /** Requests a task be transitioned from whatever mode it's in to a bubble. */
+ fun requestFloat(taskInfo: RunningTaskInfo) {
+ val isDragging = dragToDesktopTransitionHandler.inProgress
+ val shouldRequestFloat =
+ taskInfo.isFullscreen || taskInfo.isFreeform || isDragging || taskInfo.isMultiWindow
+ if (!shouldRequestFloat) return
+ if (isDragging) {
+ releaseVisualIndicator()
+ } else {
+ bubbleController.ifPresent { it.expandStackAndSelectBubble(taskInfo) }
+ }
+ }
+
private fun getDefaultDensityDpi(): Int {
return context.resources.displayMetrics.densityDpi
}
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 fd387d1..3729653 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 @@
}
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 4461a5c..7f673d2 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 @@
* 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 acb5622b..2e38449 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 @@
}
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 @@
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/taskview/TaskViewRepository.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewRepository.java
new file mode 100644
index 0000000..8cb39a0
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewRepository.java
@@ -0,0 +1,122 @@
+/*
+ * 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.taskview;
+
+import android.annotation.Nullable;
+import android.graphics.Rect;
+import android.window.WindowContainerToken;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+
+/**
+ * Keeps track of all TaskViews known by Shell. This is separated into its own object so that
+ * different TaskView managers can share state.
+ */
+public class TaskViewRepository {
+ /**
+ * The latest visibility and bounds state that has been requested for
+ * a {@link TaskViewTaskController}.
+ */
+ public static class TaskViewState {
+ final WeakReference<TaskViewTaskController> mTaskView;
+ public boolean mVisible;
+ public Rect mBounds = new Rect();
+
+ TaskViewState(TaskViewTaskController taskView) {
+ mTaskView = new WeakReference<>(taskView);
+ }
+
+ @Nullable public TaskViewTaskController getTaskView() {
+ return mTaskView.get();
+ }
+ }
+
+ /**
+ * List of tracked TaskViews
+ */
+ private final ArrayList<TaskViewState> mTaskViews = new ArrayList<>();
+
+ private int findAndPrune(TaskViewTaskController tv) {
+ for (int i = mTaskViews.size() - 1; i >= 0; --i) {
+ final TaskViewTaskController key = mTaskViews.get(i).mTaskView.get();
+ if (key == null) {
+ mTaskViews.remove(i);
+ continue;
+ }
+ if (key != tv) continue;
+ return i;
+ }
+ return -1;
+ }
+
+ /** @return if the repository is tracking {@param tv}. */
+ public boolean contains(TaskViewTaskController tv) {
+ return findAndPrune(tv) >= 0;
+ }
+
+ /** Start tracking {@param tv}. */
+ public void add(TaskViewTaskController tv) {
+ if (contains(tv)) return;
+ mTaskViews.add(new TaskViewState(tv));
+ }
+
+ /** Remove {@param tv} from tracking. */
+ public void remove(TaskViewTaskController tv) {
+ int idx = findAndPrune(tv);
+ if (idx < 0) return;
+ mTaskViews.remove(idx);
+ }
+
+ /** @return whether there are any TaskViews */
+ public boolean isEmpty() {
+ if (mTaskViews.isEmpty()) return true;
+ for (int i = mTaskViews.size() - 1; i >= 0; --i) {
+ if (mTaskViews.get(i).mTaskView.get() != null) continue;
+ mTaskViews.remove(i);
+ }
+ return mTaskViews.isEmpty();
+ }
+
+ /** @return the state of {@param tv} if tracked, {@code null} otherwise. */
+ @Nullable
+ public TaskViewState byTaskView(TaskViewTaskController tv) {
+ int idx = findAndPrune(tv);
+ if (idx < 0) return null;
+ return mTaskViews.get(idx);
+ }
+
+ /**
+ * @return the state of the taskview containing {@param token} if tracked,
+ * {@code null} otherwise.
+ */
+ @Nullable
+ public TaskViewState byToken(WindowContainerToken token) {
+ for (int i = mTaskViews.size() - 1; i >= 0; --i) {
+ final TaskViewTaskController key = mTaskViews.get(i).mTaskView.get();
+ if (key == null) {
+ mTaskViews.remove(i);
+ continue;
+ }
+ if (key.getTaskInfo() == null) continue;
+ if (key.getTaskInfo().token.equals(token)) {
+ return mTaskViews.get(i);
+ }
+ }
+ return null;
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
index eaeedba..8dd1498 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
@@ -40,6 +40,7 @@
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.wm.shell.Flags;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.SyncTransactionQueue;
@@ -619,6 +620,11 @@
return mTaskInfo;
}
+ @VisibleForTesting
+ ActivityManager.RunningTaskInfo getPendingInfo() {
+ return mPendingInfo;
+ }
+
/**
* Indicates that the task was not found in the start animation for the transition.
* In this case we should clean up the task if we have the pending info. If we don't
@@ -681,6 +687,10 @@
wct);
}
+ private TaskViewRepository.TaskViewState getState() {
+ return mTaskViewTransitions.getRepository().byTaskView(this);
+ }
+
private void prepareOpenAnimationInternal(final boolean newTask,
SurfaceControl.Transaction startTransaction,
SurfaceControl.Transaction finishTransaction,
@@ -703,8 +713,16 @@
// TODO: maybe once b/280900002 is fixed this will be unnecessary
.setWindowCrop(mTaskLeash, boundsOnScreen.width(), boundsOnScreen.height());
}
- mTaskViewTransitions.updateBoundsState(this, boundsOnScreen);
- mTaskViewTransitions.updateVisibilityState(this, true /* visible */);
+ if (TaskViewTransitions.useRepo()) {
+ final TaskViewRepository.TaskViewState state = getState();
+ if (state != null) {
+ state.mBounds.set(boundsOnScreen);
+ state.mVisible = true;
+ }
+ } else {
+ mTaskViewTransitions.updateBoundsState(this, boundsOnScreen);
+ mTaskViewTransitions.updateVisibilityState(this, true /* visible */);
+ }
wct.setBounds(mTaskToken, boundsOnScreen);
applyCaptionInsetsIfNeeded();
} else {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
index ae75cb5..0cbb7bd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
@@ -60,7 +60,8 @@
* Only keep a weak reference to the controller instance here to allow for it to be cleaned
* up when its TaskView is no longer used.
*/
- private final Map<TaskViewTaskController, TaskViewRequestedState> mTaskViews;
+ private final Map<TaskViewTaskController, TaskViewRepository.TaskViewState> mTaskViews;
+ private final TaskViewRepository mTaskViewRepo;
private final ArrayList<PendingTransition> mPending = new ArrayList<>();
private final Transitions mTransitions;
private final boolean[] mRegistered = new boolean[]{false};
@@ -95,26 +96,29 @@
}
}
- /**
- * Visibility and bounds state that has been requested for a {@link TaskViewTaskController}.
- */
- private static class TaskViewRequestedState {
- boolean mVisible;
- Rect mBounds = new Rect();
- }
-
- public TaskViewTransitions(Transitions transitions) {
+ public TaskViewTransitions(Transitions transitions, TaskViewRepository repository) {
mTransitions = transitions;
- if (Flags.enableTaskViewControllerCleanup()) {
+ if (useRepo()) {
+ mTaskViews = null;
+ } else if (Flags.enableTaskViewControllerCleanup()) {
mTaskViews = new WeakHashMap<>();
} else {
mTaskViews = new ArrayMap<>();
}
+ mTaskViewRepo = repository;
// Defer registration until the first TaskView because we want this to be the "first" in
// priority when handling requests.
// TODO(210041388): register here once we have an explicit ordering mechanism.
}
+ static boolean useRepo() {
+ return Flags.taskViewRepository() || Flags.enableBubbleAnything();
+ }
+
+ public TaskViewRepository getRepository() {
+ return mTaskViewRepo;
+ }
+
void addTaskView(TaskViewTaskController tv) {
synchronized (mRegistered) {
if (!mRegistered[0]) {
@@ -122,11 +126,19 @@
mTransitions.addHandler(this);
}
}
- mTaskViews.put(tv, new TaskViewRequestedState());
+ if (useRepo()) {
+ mTaskViewRepo.add(tv);
+ } else {
+ mTaskViews.put(tv, new TaskViewRepository.TaskViewState(null));
+ }
}
void removeTaskView(TaskViewTaskController tv) {
- mTaskViews.remove(tv);
+ if (useRepo()) {
+ mTaskViewRepo.remove(tv);
+ } else {
+ mTaskViews.remove(tv);
+ }
// Note: Don't unregister handler since this is a singleton with lifetime bound to Shell
}
@@ -223,6 +235,10 @@
}
private TaskViewTaskController findTaskView(ActivityManager.RunningTaskInfo taskInfo) {
+ if (useRepo()) {
+ final TaskViewRepository.TaskViewState state = mTaskViewRepo.byToken(taskInfo.token);
+ return state != null ? state.getTaskView() : null;
+ }
if (Flags.enableTaskViewControllerCleanup()) {
for (TaskViewTaskController controller : mTaskViews.keySet()) {
if (controller.getTaskInfo() == null) continue;
@@ -231,8 +247,8 @@
}
}
} else {
- ArrayMap<TaskViewTaskController, TaskViewRequestedState> taskViews =
- (ArrayMap<TaskViewTaskController, TaskViewRequestedState>) mTaskViews;
+ ArrayMap<TaskViewTaskController, TaskViewRepository.TaskViewState> taskViews =
+ (ArrayMap<TaskViewTaskController, TaskViewRepository.TaskViewState>) mTaskViews;
for (int i = 0; i < taskViews.size(); ++i) {
if (taskViews.keyAt(i).getTaskInfo() == null) continue;
if (taskInfo.token.equals(taskViews.keyAt(i).getTaskInfo().token)) {
@@ -279,23 +295,26 @@
*
* @param taskView the task view which the visibility is being changed for
* @param visible the new visibility of the task view
- * @param reorder whether to reorder the task or not. If this is {@code true}, the task will be
- * reordered as per the given {@code visible}. For {@code visible = true}, task
- * will be reordered to top. For {@code visible = false}, task will be reordered
- * to the bottom
+ * @param reorder whether to reorder the task or not. If this is {@code true}, the task will
+ * be reordered as per the given {@code visible}. For {@code visible = true},
+ * task will be reordered to top. For {@code visible = false}, task will be
+ * reordered to the bottom
*/
public void setTaskViewVisible(TaskViewTaskController taskView, boolean visible,
boolean reorder) {
- if (mTaskViews.get(taskView) == null) return;
- if (mTaskViews.get(taskView).mVisible == visible) return;
+ final TaskViewRepository.TaskViewState state = useRepo()
+ ? mTaskViewRepo.byTaskView(taskView)
+ : mTaskViews.get(taskView);
+ if (state == null) return;
+ if (state.mVisible == visible) return;
if (taskView.getTaskInfo() == null) {
// Nothing to update, task is not yet available
return;
}
- mTaskViews.get(taskView).mVisible = visible;
+ state.mVisible = visible;
final WindowContainerTransaction wct = new WindowContainerTransaction();
wct.setHidden(taskView.getTaskInfo().token, !visible /* hidden */);
- wct.setBounds(taskView.getTaskInfo().token, mTaskViews.get(taskView).mBounds);
+ wct.setBounds(taskView.getTaskInfo().token, state.mBounds);
if (reorder) {
wct.reorder(taskView.getTaskInfo().token, visible /* onTop */);
}
@@ -308,7 +327,10 @@
/** Starts a new transition to reorder the given {@code taskView}'s task. */
public void reorderTaskViewTask(TaskViewTaskController taskView, boolean onTop) {
- if (mTaskViews.get(taskView) == null) return;
+ final TaskViewRepository.TaskViewState state = useRepo()
+ ? mTaskViewRepo.byTaskView(taskView)
+ : mTaskViews.get(taskView);
+ if (state == null) return;
if (taskView.getTaskInfo() == null) {
// Nothing to update, task is not yet available
return;
@@ -323,19 +345,24 @@
}
void updateBoundsState(TaskViewTaskController taskView, Rect boundsOnScreen) {
- TaskViewRequestedState state = mTaskViews.get(taskView);
+ if (useRepo()) return;
+ final TaskViewRepository.TaskViewState state = mTaskViews.get(taskView);
if (state == null) return;
state.mBounds.set(boundsOnScreen);
}
void updateVisibilityState(TaskViewTaskController taskView, boolean visible) {
- TaskViewRequestedState state = mTaskViews.get(taskView);
+ final TaskViewRepository.TaskViewState state = useRepo()
+ ? mTaskViewRepo.byTaskView(taskView)
+ : mTaskViews.get(taskView);
if (state == null) return;
state.mVisible = visible;
}
void setTaskBounds(TaskViewTaskController taskView, Rect boundsOnScreen) {
- TaskViewRequestedState state = mTaskViews.get(taskView);
+ final TaskViewRepository.TaskViewState state = useRepo()
+ ? mTaskViewRepo.byTaskView(taskView)
+ : mTaskViews.get(taskView);
if (state == null || Objects.equals(boundsOnScreen, state.mBounds)) {
return;
}
@@ -385,7 +412,7 @@
if (pending != null) {
mPending.remove(pending);
}
- if (mTaskViews.isEmpty()) {
+ if (useRepo() ? mTaskViewRepo.isEmpty() : mTaskViews.isEmpty()) {
if (pending != null) {
Slog.e(TAG, "Pending taskview transition but no task-views");
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index e80016d..1689bb5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -329,7 +329,7 @@
@ColorInt int backgroundColorForTransition = 0;
final int wallpaperTransit = getWallpaperTransitType(info);
- boolean isDisplayRotationAnimationStarted = false;
+ int animatingDisplayId = Integer.MIN_VALUE;
final boolean isDreamTransition = isDreamTransition(info);
final boolean isOnlyTranslucent = isOnlyTranslucent(info);
final boolean isActivityLevel = isActivityLevelOnly(info);
@@ -361,7 +361,7 @@
? ScreenRotationAnimation.FLAG_HAS_WALLPAPER : 0;
startRotationAnimation(startTransaction, change, info, anim, flags,
animations, onAnimFinish);
- isDisplayRotationAnimationStarted = true;
+ animatingDisplayId = change.getEndDisplayId();
continue;
}
} else {
@@ -426,8 +426,11 @@
// Hide the invisible surface directly without animating it if there is a display
// rotation animation playing.
- if (isDisplayRotationAnimationStarted && TransitionUtil.isClosingType(mode)) {
- startTransaction.hide(change.getLeash());
+ if (animatingDisplayId == change.getEndDisplayId()) {
+ if (TransitionUtil.isClosingType(mode)) {
+ startTransaction.hide(change.getLeash());
+ }
+ // Only need to play display level animation.
continue;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index df81821..edb2e1c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -546,8 +546,13 @@
// When the window is moved to front, make sure the crop is updated to prevent it
// from using the old crop.
t.setPosition(leash, change.getEndRelOffset().x, change.getEndRelOffset().y);
- t.setWindowCrop(leash, change.getEndAbsBounds().width(),
- change.getEndAbsBounds().height());
+ if (change.getContainer() != null) {
+ // We don't want to crop on non-remotable (activity), because it can have
+ // letterbox child surface that is position at a negative position related to
+ // the activity's surface.
+ t.setWindowCrop(leash, change.getEndAbsBounds().width(),
+ change.getEndAbsBounds().height());
+ }
}
// Don't move anything that isn't independent within its parents
@@ -557,8 +562,13 @@
t.setMatrix(leash, 1, 0, 0, 1);
t.setAlpha(leash, 1.f);
t.setPosition(leash, change.getEndRelOffset().x, change.getEndRelOffset().y);
- t.setWindowCrop(leash, change.getEndAbsBounds().width(),
- change.getEndAbsBounds().height());
+ if (change.getContainer() != null) {
+ // We don't want to crop on non-remotable (activity), because it can have
+ // letterbox child surface that is position at a negative position related
+ // to the activity's surface.
+ t.setWindowCrop(leash, change.getEndAbsBounds().width(),
+ change.getEndAbsBounds().height());
+ }
}
continue;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index a7a5f09..046cb20 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -776,6 +776,18 @@
DesktopUiEventEnum.DESKTOP_WINDOW_APP_HANDLE_MENU_TAP_TO_SPLIT_SCREEN);
}
+ private void onToFloat(int taskId) {
+ final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(taskId);
+ if (decoration == null) {
+ return;
+ }
+ decoration.closeHandleMenu();
+ // When the app enters float, the handle will no longer be visible, meaning
+ // we shouldn't receive input for it any longer.
+ decoration.disposeStatusBarInputLayer();
+ mDesktopTasksController.requestFloat(decoration.mTaskInfo);
+ }
+
private void onNewWindow(int taskId) {
final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(taskId);
if (decoration == null) {
@@ -1731,6 +1743,10 @@
onToSplitScreen(taskInfo.taskId);
return Unit.INSTANCE;
});
+ windowDecoration.setOnToFloatClickListener(() -> {
+ onToFloat(taskInfo.taskId);
+ return Unit.INSTANCE;
+ });
windowDecoration.setOpenInBrowserClickListener((intent) -> {
onOpenInBrowser(taskInfo.taskId, intent);
});
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index 0d1960a..4ac8954 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -155,6 +155,7 @@
private Consumer<DesktopModeTransitionSource> mOnToDesktopClickListener;
private Function0<Unit> mOnToFullscreenClickListener;
private Function0<Unit> mOnToSplitscreenClickListener;
+ private Function0<Unit> mOnToFloatClickListener;
private Function0<Unit> mOnNewWindowClickListener;
private Function0<Unit> mOnManageWindowsClickListener;
private Function0<Unit> mOnChangeAspectRatioClickListener;
@@ -351,6 +352,11 @@
mOnToSplitscreenClickListener = listener;
}
+ /** Registers a listener to be called when the decoration's to-split action is triggered. */
+ void setOnToFloatClickListener(Function0<Unit> listener) {
+ mOnToFloatClickListener = listener;
+ }
+
/**
* Adds a drag resize observer that gets notified on the task being drag resized.
*
@@ -1372,6 +1378,7 @@
},
/* onToFullscreenClickListener= */ mOnToFullscreenClickListener,
/* onToSplitScreenClickListener= */ mOnToSplitscreenClickListener,
+ /* onToFloatClickListener= */ mOnToFloatClickListener,
/* onNewWindowClickListener= */ mOnNewWindowClickListener,
/* onManageWindowsClickListener= */ mOnManageWindowsClickListener,
/* onAspectRatioSettingsClickListener= */ mOnChangeAspectRatioClickListener,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
index bb19a2c..9d73950 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
@@ -144,6 +144,7 @@
onToDesktopClickListener: () -> Unit,
onToFullscreenClickListener: () -> Unit,
onToSplitScreenClickListener: () -> Unit,
+ onToFloatClickListener: () -> Unit,
onNewWindowClickListener: () -> Unit,
onManageWindowsClickListener: () -> Unit,
onChangeAspectRatioClickListener: () -> Unit,
@@ -162,6 +163,7 @@
onToDesktopClickListener = onToDesktopClickListener,
onToFullscreenClickListener = onToFullscreenClickListener,
onToSplitScreenClickListener = onToSplitScreenClickListener,
+ onToFloatClickListener = onToFloatClickListener,
onNewWindowClickListener = onNewWindowClickListener,
onManageWindowsClickListener = onManageWindowsClickListener,
onChangeAspectRatioClickListener = onChangeAspectRatioClickListener,
@@ -183,6 +185,7 @@
onToDesktopClickListener: () -> Unit,
onToFullscreenClickListener: () -> Unit,
onToSplitScreenClickListener: () -> Unit,
+ onToFloatClickListener: () -> Unit,
onNewWindowClickListener: () -> Unit,
onManageWindowsClickListener: () -> Unit,
onChangeAspectRatioClickListener: () -> Unit,
@@ -208,6 +211,7 @@
this.onToDesktopClickListener = onToDesktopClickListener
this.onToFullscreenClickListener = onToFullscreenClickListener
this.onToSplitScreenClickListener = onToSplitScreenClickListener
+ this.onToFloatClickListener = onToFloatClickListener
this.onNewWindowClickListener = onNewWindowClickListener
this.onManageWindowsClickListener = onManageWindowsClickListener
this.onChangeAspectRatioClickListener = onChangeAspectRatioClickListener
@@ -502,6 +506,7 @@
var onToDesktopClickListener: (() -> Unit)? = null
var onToFullscreenClickListener: (() -> Unit)? = null
var onToSplitScreenClickListener: (() -> Unit)? = null
+ var onToFloatClickListener: (() -> Unit)? = null
var onNewWindowClickListener: (() -> Unit)? = null
var onManageWindowsClickListener: (() -> Unit)? = null
var onChangeAspectRatioClickListener: (() -> Unit)? = null
@@ -515,6 +520,7 @@
splitscreenBtn.setOnClickListener { onToSplitScreenClickListener?.invoke() }
desktopBtn.setOnClickListener { onToDesktopClickListener?.invoke() }
openInAppOrBrowserBtn.setOnClickListener { onOpenInAppOrBrowserClickListener?.invoke() }
+ floatingBtn.setOnClickListener { onToFloatClickListener?.invoke() }
openByDefaultBtn.setOnClickListener {
onOpenByDefaultClickListener?.invoke()
}
@@ -640,8 +646,9 @@
private fun bindWindowingPill(style: MenuStyle) {
windowingPill.background.setTint(style.backgroundColor)
- // TODO: Remove once implemented.
- floatingBtn.visibility = View.GONE
+ if (!com.android.wm.shell.Flags.enableBubbleAnything()) {
+ floatingBtn.visibility = View.GONE
+ }
fullscreenBtn.isSelected = taskInfo.isFullscreen
fullscreenBtn.isEnabled = !taskInfo.isFullscreen
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/EnterDesktopViaMenuOfStaticOverviewTaskTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/EnterDesktopViaMenuOfStaticOverviewTaskTest.kt
new file mode 100644
index 0000000..fac749b
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/EnterDesktopViaMenuOfStaticOverviewTaskTest.kt
@@ -0,0 +1,27 @@
+/*
+ * 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.functional
+
+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 0000000..57647d6
--- /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/unittest/src/com/android/wm/shell/bubbles/BubbleViewInfoTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleViewInfoTest.kt
index 1f2eaa6..eeb83df 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.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 @@
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/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index fe08526..e032616 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -93,6 +93,7 @@
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.TestRunningTaskInfoBuilder
import com.android.wm.shell.TestShellExecutor
+import com.android.wm.shell.bubbles.BubbleController
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayLayout
import com.android.wm.shell.common.MultiInstanceHelper
@@ -232,6 +233,7 @@
@Mock private lateinit var mockToast: Toast
private lateinit var mockitoSession: StaticMockitoSession
@Mock private lateinit var desktopTilingDecorViewModel: DesktopTilingDecorViewModel
+ @Mock private lateinit var bubbleController: BubbleController
@Mock private lateinit var desktopWindowDecoration: DesktopModeWindowDecoration
@Mock private lateinit var resources: Resources
@Mock
@@ -383,6 +385,7 @@
desktopModeUiEventLogger,
desktopTilingDecorViewModel,
desktopWallpaperActivityTokenProvider,
+ Optional.of(bubbleController),
)
}
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 3fe8c10..a8aa257 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 @@
@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 @@
@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/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
index 17fd95b..66636c5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
@@ -20,6 +20,11 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
@@ -45,7 +50,8 @@
import android.graphics.Rect;
import android.graphics.Region;
import android.os.Looper;
-import android.testing.AndroidTestingRunner;
+import android.platform.test.flag.junit.FlagsParameterization;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.testing.TestableLooper;
import android.view.SurfaceControl;
import android.view.SurfaceHolder;
@@ -55,6 +61,7 @@
import androidx.test.filters.SmallTest;
+import com.android.wm.shell.Flags;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestHandler;
@@ -65,17 +72,33 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
+import java.util.List;
+
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(ParameterizedAndroidJunit4.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class TaskViewTest extends ShellTestCase {
+ @Parameters(name = "{0}")
+ public static List<FlagsParameterization> getParams() {
+ return FlagsParameterization.allCombinationsOf(Flags.FLAG_TASK_VIEW_REPOSITORY);
+ }
+
+ @Rule
+ public final SetFlagsRule mSetFlagsRule;
+
@Mock
TaskView.Listener mViewListener;
@Mock
@@ -84,6 +107,8 @@
WindowContainerToken mToken;
@Mock
ShellTaskOrganizer mOrganizer;
+ @Captor
+ ArgumentCaptor<WindowContainerTransaction> mWctCaptor;
@Mock
HandlerExecutor mExecutor;
@Mock
@@ -98,9 +123,14 @@
Context mContext;
TaskView mTaskView;
+ TaskViewRepository mTaskViewRepository;
TaskViewTransitions mTaskViewTransitions;
TaskViewTaskController mTaskViewTaskController;
+ public TaskViewTest(FlagsParameterization flags) {
+ mSetFlagsRule = new SetFlagsRule(flags);
+ }
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
@@ -134,9 +164,10 @@
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
doReturn(true).when(mTransitions).isRegistered();
}
- mTaskViewTransitions = spy(new TaskViewTransitions(mTransitions));
- mTaskViewTaskController = spy(new TaskViewTaskController(mContext, mOrganizer,
- mTaskViewTransitions, mSyncQueue));
+ mTaskViewRepository = new TaskViewRepository();
+ mTaskViewTransitions = spy(new TaskViewTransitions(mTransitions, mTaskViewRepository));
+ mTaskViewTaskController = new TaskViewTaskController(mContext, mOrganizer,
+ mTaskViewTransitions, mSyncQueue);
mTaskView = new TaskView(mContext, mTaskViewTaskController);
mTaskView.setHandler(mViewHandler);
mTaskView.setListener(mExecutor, mViewListener);
@@ -482,8 +513,14 @@
// Surface created, but task not available so bounds / visibility isn't set
mTaskView.surfaceCreated(mock(SurfaceHolder.class));
- verify(mTaskViewTransitions, never()).updateVisibilityState(
- eq(mTaskViewTaskController), eq(true));
+ if (TaskViewTransitions.useRepo()) {
+ assertNotNull(mTaskViewTransitions.getRepository().byTaskView(mTaskViewTaskController));
+ assertFalse(mTaskViewTransitions.getRepository().byTaskView(mTaskViewTaskController)
+ .mVisible);
+ } else {
+ verify(mTaskViewTransitions, never()).updateVisibilityState(
+ eq(mTaskViewTaskController), eq(true));
+ }
// Make the task available
WindowContainerTransaction wct = mock(WindowContainerTransaction.class);
@@ -492,8 +529,16 @@
// Bounds got set
verify(wct).setBounds(any(WindowContainerToken.class), eq(bounds));
// Visibility & bounds state got set
- verify(mTaskViewTransitions).updateVisibilityState(eq(mTaskViewTaskController), eq(true));
- verify(mTaskViewTransitions).updateBoundsState(eq(mTaskViewTaskController), eq(bounds));
+ if (TaskViewTransitions.useRepo()) {
+ assertTrue(mTaskViewTransitions.getRepository().byTaskView(mTaskViewTaskController)
+ .mVisible);
+ assertEquals(mTaskViewTransitions.getRepository().byTaskView(mTaskViewTaskController)
+ .mBounds, bounds);
+ } else {
+ verify(mTaskViewTransitions).updateVisibilityState(eq(mTaskViewTaskController),
+ eq(true));
+ verify(mTaskViewTransitions).updateBoundsState(eq(mTaskViewTaskController), eq(bounds));
+ }
}
@Test
@@ -507,8 +552,15 @@
// Surface created, but task not available so bounds / visibility isn't set
mTaskView.surfaceCreated(mock(SurfaceHolder.class));
- verify(mTaskViewTransitions, never()).updateVisibilityState(
- eq(mTaskViewTaskController), eq(true));
+ if (TaskViewTransitions.useRepo()) {
+ assertNotNull(mTaskViewTransitions.getRepository().byTaskView(
+ mTaskViewTaskController));
+ assertFalse(mTaskViewTransitions.getRepository().byTaskView(mTaskViewTaskController)
+ .mVisible);
+ } else {
+ verify(mTaskViewTransitions, never()).updateVisibilityState(
+ eq(mTaskViewTaskController), eq(true));
+ }
// Make the task available / start prepareOpen
WindowContainerTransaction wct = mock(WindowContainerTransaction.class);
@@ -519,8 +571,16 @@
// Bounds got set
verify(wct).setBounds(any(WindowContainerToken.class), eq(bounds));
// Visibility & bounds state got set
- verify(mTaskViewTransitions).updateVisibilityState(eq(mTaskViewTaskController), eq(true));
- verify(mTaskViewTransitions).updateBoundsState(eq(mTaskViewTaskController), eq(bounds));
+ if (TaskViewTransitions.useRepo()) {
+ assertTrue(mTaskViewTransitions.getRepository().byTaskView(mTaskViewTaskController)
+ .mVisible);
+ assertEquals(mTaskViewTransitions.getRepository().byTaskView(mTaskViewTaskController)
+ .mBounds, bounds);
+ } else {
+ verify(mTaskViewTransitions).updateVisibilityState(eq(mTaskViewTaskController),
+ eq(true));
+ verify(mTaskViewTransitions).updateBoundsState(eq(mTaskViewTaskController), eq(bounds));
+ }
}
@Test
@@ -541,8 +601,17 @@
// Bounds do not get set as there is no surface
verify(wct, never()).setBounds(any(WindowContainerToken.class), any());
// Visibility is set to false, bounds aren't set
- verify(mTaskViewTransitions).updateVisibilityState(eq(mTaskViewTaskController), eq(false));
- verify(mTaskViewTransitions, never()).updateBoundsState(eq(mTaskViewTaskController), any());
+ if (TaskViewTransitions.useRepo()) {
+ assertFalse(mTaskViewTransitions.getRepository().byTaskView(mTaskViewTaskController)
+ .mVisible);
+ assertTrue(mTaskViewTransitions.getRepository().byTaskView(mTaskViewTaskController)
+ .mBounds.isEmpty());
+ } else {
+ verify(mTaskViewTransitions).updateVisibilityState(eq(mTaskViewTaskController),
+ eq(false));
+ verify(mTaskViewTransitions, never()).updateBoundsState(eq(mTaskViewTaskController),
+ any());
+ }
}
@Test
@@ -576,7 +645,7 @@
mTaskViewTaskController.setTaskNotFound();
mTaskViewTaskController.onTaskAppeared(mTaskInfo, mLeash);
- verify(mTaskViewTaskController).cleanUpPendingTask();
+ assertNull(mTaskViewTaskController.getTaskInfo());
verify(mTaskViewTransitions).closeTaskView(any(), eq(mTaskViewTaskController));
}
@@ -585,7 +654,8 @@
assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
mTaskViewTaskController.onTaskAppeared(mTaskInfo, mLeash);
- verify(mTaskViewTaskController, never()).cleanUpPendingTask();
+ assertEquals(mTaskInfo, mTaskViewTaskController.getPendingInfo());
+ verify(mTaskViewTransitions, never()).closeTaskView(any(), any());
}
@Test
@@ -596,20 +666,20 @@
mTaskView.setCaptionInsets(Insets.of(insets));
mTaskView.onComputeInternalInsets(new ViewTreeObserver.InternalInsetsInfo());
- verify(mTaskViewTaskController).applyCaptionInsetsIfNeeded();
verify(mOrganizer, never()).applyTransaction(any());
mTaskView.surfaceCreated(mock(SurfaceHolder.class));
reset(mOrganizer);
- reset(mTaskViewTaskController);
WindowContainerTransaction wct = new WindowContainerTransaction();
mTaskViewTaskController.prepareOpenAnimation(true /* newTask */,
new SurfaceControl.Transaction(), new SurfaceControl.Transaction(), mTaskInfo,
mLeash, wct);
mTaskView.onComputeInternalInsets(new ViewTreeObserver.InternalInsetsInfo());
- verify(mTaskViewTaskController).applyCaptionInsetsIfNeeded();
- verify(mOrganizer).applyTransaction(any());
+ verify(mOrganizer).applyTransaction(mWctCaptor.capture());
+ assertTrue(mWctCaptor.getValue().getHierarchyOps().stream().anyMatch(hop ->
+ hop.getType() == WindowContainerTransaction.HierarchyOp
+ .HIERARCHY_OP_TYPE_ADD_INSETS_FRAME_PROVIDER));
}
@Test
@@ -621,14 +691,15 @@
mTaskViewTaskController.prepareOpenAnimation(true /* newTask */,
new SurfaceControl.Transaction(), new SurfaceControl.Transaction(), mTaskInfo,
mLeash, wct);
- reset(mTaskViewTaskController);
reset(mOrganizer);
Rect insets = new Rect(0, 400, 0, 0);
mTaskView.setCaptionInsets(Insets.of(insets));
mTaskView.onComputeInternalInsets(new ViewTreeObserver.InternalInsetsInfo());
- verify(mTaskViewTaskController).applyCaptionInsetsIfNeeded();
- verify(mOrganizer).applyTransaction(any());
+ verify(mOrganizer).applyTransaction(mWctCaptor.capture());
+ assertTrue(mWctCaptor.getValue().getHierarchyOps().stream().anyMatch(hop ->
+ hop.getType() == WindowContainerTransaction.HierarchyOp
+ .HIERARCHY_OP_TYPE_ADD_INSETS_FRAME_PROVIDER));
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java
index 60e2030..5f6f18f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java
@@ -83,6 +83,7 @@
@Mock
WindowContainerToken mToken;
+ TaskViewRepository mTaskViewRepository;
TaskViewTransitions mTaskViewTransitions;
public TaskViewTransitionsTest(FlagsParameterization flags) {
@@ -102,7 +103,8 @@
mTaskInfo.taskId = 314;
mTaskInfo.taskDescription = mock(ActivityManager.TaskDescription.class);
- mTaskViewTransitions = spy(new TaskViewTransitions(mTransitions));
+ mTaskViewRepository = new TaskViewRepository();
+ mTaskViewTransitions = spy(new TaskViewTransitions(mTransitions, mTaskViewRepository));
mTaskViewTransitions.addTaskView(mTaskViewTaskController);
when(mTaskViewTaskController.getTaskInfo()).thenReturn(mTaskInfo);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
index e99e5cc..7dac085 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
@@ -1091,6 +1091,7 @@
any(),
any(),
any(),
+ any(),
openInBrowserCaptor.capture(),
any(),
any(),
@@ -1127,6 +1128,7 @@
any(),
any(),
any(),
+ any(),
openInBrowserCaptor.capture(),
any(),
any(),
@@ -1158,6 +1160,7 @@
any(),
any(),
any(),
+ any(),
openInBrowserCaptor.capture(),
any(),
any(),
@@ -1226,6 +1229,7 @@
any(),
any(),
any(),
+ any(),
closeClickListener.capture(),
any(),
anyBoolean()
@@ -1258,6 +1262,7 @@
any(),
any(),
any(),
+ any(),
/* forceShowSystemBars= */ eq(true)
);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
index cbfb57e..f90988e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
@@ -307,6 +307,7 @@
onToDesktopClickListener = mock(),
onToFullscreenClickListener = mock(),
onToSplitScreenClickListener = mock(),
+ onToFloatClickListener = mock(),
onNewWindowClickListener = mock(),
onManageWindowsClickListener = mock(),
onChangeAspectRatioClickListener = mock(),
diff --git a/media/java/android/media/projection/MediaProjection.java b/media/java/android/media/projection/MediaProjection.java
index f7f10df..4f7132a 100644
--- a/media/java/android/media/projection/MediaProjection.java
+++ b/media/java/android/media/projection/MediaProjection.java
@@ -324,6 +324,19 @@
}
/**
+ * 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/libandroid.map.txt b/native/android/libandroid.map.txt
index 1ccadf9..f629c88 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -396,6 +396,7 @@
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 @@
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 9257901..7e65523 100644
--- a/native/android/performance_hint.cpp
+++ b/native/android/performance_hint.cpp
@@ -71,26 +71,31 @@
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 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
// ===================================== 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 @@
.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 @@
}
}
+ 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 @@
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 @@
}
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 @@
.workPeriodStartTimestampNanos = 0,
.cpuDurationNanos = actualDurationNanos,
.gpuDurationNanos = 0};
-
return reportActualWorkDurationInternal(static_cast<AWorkDuration*>(&workDuration));
}
@@ -556,17 +607,24 @@
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;
- }
- 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;
- }
+ // We successfully sent all hints we were able to, technically
+ return 0;
}
+ for (auto&& hint : hints) {
+ 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 @@
// 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 @@
}
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 @@
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::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 @@
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 @@
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 @@
}
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 @@
ATrace_setCounter((mSessionName + " target duration").c_str(), targetDuration);
}
-// ===================================== C API
+// ===================================== Start of C API
+
APerformanceHintManager* APerformanceHint_getManager() {
return APerformanceHintManager::getInstance();
}
@@ -1037,10 +1114,16 @@
#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 @@
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_updateTargetWorkDuration(APerformanceHintSession* session,
int64_t targetDurationNanos) {
VALIDATE_PTR(session)
+ VALIDATE_INT(targetDurationNanos, >= 0)
return session->updateTargetWorkDuration(targetDurationNanos);
}
@@ -1204,13 +1296,23 @@
}
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 @@
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 @@
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 e3c10f6..f68fa1a 100644
--- a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
+++ b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
@@ -49,12 +49,87 @@
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 @@
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 @@
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 @@
ON_CALL(*mMockSession, reportActualWorkDuration2(_)).WillByDefault([] {
return ScopedAStatus::ok();
});
- if (isHwui) {
- return APerformanceHint_createSessionInternal(manager, tids.data(), tids.size(),
- targetDuration, SessionTag::HWUI);
- }
- return APerformanceHint_createSession(manager, tids.data(), tids.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> createSession(APerformanceHintManager* manager,
+ int64_t targetDuration = 56789L,
+ bool isHwui = false) {
+ prepareSessionMock();
if (isHwui) {
- return APerformanceHint_createSessionUsingConfigInternal(
- manager, reinterpret_cast<ASessionCreationConfig*>(&config), SessionTag::HWUI);
+ return wrapSession(APerformanceHint_createSessionInternal(manager, mTids.data(),
+ mTids.size(), targetDuration,
+ SessionTag::HWUI));
+ }
+ return wrapSession(APerformanceHint_createSession(manager, mTids.data(), mTids.size(),
+ targetDuration));
+ }
+
+ std::shared_ptr<APerformanceHintSession> createSessionUsingConfig(
+ APerformanceHintManager* manager, std::shared_ptr<ASessionCreationConfig>& config,
+ bool isHwui = false) {
+ prepareSessionMock();
+ APerformanceHintSession* session;
+ int out = 0;
+ if (isHwui) {
+ 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
.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();
- ASSERT_NE(config, nullptr);
+ auto&& config = configFromCreator({
+ .tids = mTids,
+ .targetDuration = 20,
+ .powerEfficient = true,
+ .graphicsPipeline = true,
+ });
- 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);
+ 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();
+
+ 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 0fdb3bd..0000000
--- 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 f46dccd..0000000
--- 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 49c778d..0000000
--- 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 c8c479a..0000000
--- 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 ef9aab6..0000000
--- 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 5ebe911..0000000
--- 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 f7f8ee3..0000000
--- 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 d802177..0000000
--- 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 fb82b5d..0000000
--- 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 6e69da1..0000000
--- 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 c7a6181..0000000
--- 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 c6eaa57..0000000
--- 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 d802177..0000000
--- 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 d802177..0000000
--- 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 63a6a58..0000000
--- 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 f6236b2..0000000
--- 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 027141d..0000000
--- 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 9d06e2d..0000000
--- 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 e76aeb0..0000000
--- 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 e677998..0000000
--- 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 59e6c62..0000000
--- 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 9b11e2d..0000000
--- 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 148c4ec..0000000
--- 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 aa5ba58..0000000
--- 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 d2c81cd..0000000
--- 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 a57de1e..0000000
--- 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 b06bf06..0000000
--- 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 ac0a5aa..0000000
--- 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 cde57c5..0000000
--- 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 00ceaa9..0000000
--- 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 1bb7680..0000000
--- 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 4cc5927..0000000
--- 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 af1fa2fb..0000000
--- 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 124bfac..0000000
--- 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 357d322..0000000
--- 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 170df71..0000000
--- 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 e1cace9..0000000
--- 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 821dc6f..0000000
--- 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 584eb9a..0000000
--- 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 b4cda5b..0000000
--- 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 2a06ff3..0000000
--- 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 378b9d0..0000000
--- 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 553f6c0..0000000
--- 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 10f89d0..0000000
--- 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 7bf4355..0000000
--- 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 909eca7..0000000
--- 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 63397c2..0000000
--- 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 d5e79fc..0000000
--- 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 c57b2e0..0000000
--- 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 6ae58fd..0000000
--- 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 aff4f52..0000000
--- 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 1ab8a1e..0000000
--- 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 644e312..0000000
--- 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 1fc0786..0000000
--- 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 4153779..0000000
--- 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 742d75f..0000000
--- 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 890cb09..0000000
--- 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 3bcb445..0000000
--- 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 4f3e199..0000000
--- 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 3509644..0000000
--- 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 4a1b1f3..0000000
--- 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 be94f9f..0000000
--- 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 a68d8c1..0000000
--- 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 06cc0a5..0000000
--- 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 86239ce..0000000
--- 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 05a30aa..0000000
--- 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.aidl b/nfc/java/android/nfc/T4tNdefNfceeCcFileInfo.aidl
deleted file mode 100644
index f72f74e..0000000
--- a/nfc/java/android/nfc/T4tNdefNfceeCcFileInfo.aidl
+++ /dev/null
@@ -1,20 +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 T4tNdefNfceeCcFileInfo;
-
diff --git a/nfc/java/android/nfc/T4tNdefNfceeCcFileInfo.java b/nfc/java/android/nfc/T4tNdefNfceeCcFileInfo.java
deleted file mode 100644
index ce67f8f..0000000
--- 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 312261e..0000000
--- 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 500038f..0000000
--- 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 1981d7c..0000000
--- 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 92e646f..0000000
--- 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 9f01559..0000000
--- 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 98f92ee..0000000
--- 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 7992094..0000000
--- 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 7f2ca54..0000000
--- 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 45315f8..0000000
--- 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 fee9c5b..0000000
--- 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 fbf2203..0000000
--- 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 65b5ca7..0000000
--- 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 48bbf5b6..0000000
--- 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 8d8a172..0000000
--- 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 8e09f8b..0000000
--- 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 5dcc84c..0000000
--- 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 202e1cf..0000000
--- 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 8801662..0000000
--- 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 55c1d16..0000000
--- 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 ae468fe..0000000
--- 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 0ba0c5a..0000000
--- 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 26f54e6..0000000
--- 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 c0416a3..0000000
--- 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 7d83f15..0000000
--- 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 2240fe7..0000000
--- 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 7e66483..0000000
--- 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 3ebd47f..0000000
--- 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 421ba78..0000000
--- 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 2ccd388..0000000
--- 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 186c63b..0000000
--- 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 839fe42..0000000
--- 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 a99828f..0000000
--- 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 67b496e..0000000
--- 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 17fb810..0000000
--- 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 9564672..0000000
--- 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 490d6f5..0000000
--- 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 9ca295d..0000000
--- 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 044c674..0000000
--- 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 c24816d..0000000
--- 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 48f4288..0000000
--- 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 06314cc..0000000
--- 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 a90a716..0000000
--- 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 21ef804..0000000
--- 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 a12bbbc..0000000
--- 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 7e00102..0000000
--- 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 4863206..0000000
--- 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 38fb7ef..0000000
--- 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 40076eb..0000000
--- 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 98d6070..0000000
--- 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 3aa4e2c..0000000
--- 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 31a6943..0000000
--- 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 6a99921..0000000
--- 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/SettingsLib/Metadata/processor/src/com/android/settingslib/metadata/PreferenceScreenAnnotationProcessor.kt b/packages/SettingsLib/Metadata/processor/src/com/android/settingslib/metadata/PreferenceScreenAnnotationProcessor.kt
index 620d717..7432254 100644
--- a/packages/SettingsLib/Metadata/processor/src/com/android/settingslib/metadata/PreferenceScreenAnnotationProcessor.kt
+++ b/packages/SettingsLib/Metadata/processor/src/com/android/settingslib/metadata/PreferenceScreenAnnotationProcessor.kt
@@ -129,7 +129,15 @@
)
}
}
- processingEnv.filer.createSourceFile("$outputPkg.$outputClass").openWriter().use {
+ val javaFileObject =
+ try {
+ processingEnv.filer.createSourceFile("$outputPkg.$outputClass")
+ } catch (e: Exception) {
+ // quick fix: gradle runs this processor twice unexpectedly
+ warn("cannot createSourceFile: $e")
+ return
+ }
+ javaFileObject.openWriter().use {
it.write("package $outputPkg;\n\n")
it.write("import $PACKAGE.$PREFERENCE_SCREEN_METADATA;\n\n")
it.write("// Generated by annotation processor for @$ANNOTATION_NAME\n")
diff --git a/packages/SettingsLib/SliderPreference/src/com/android/settingslib/widget/SliderPreference.java b/packages/SettingsLib/SliderPreference/src/com/android/settingslib/widget/SliderPreference.java
index 1815d04..4315238 100644
--- a/packages/SettingsLib/SliderPreference/src/com/android/settingslib/widget/SliderPreference.java
+++ b/packages/SettingsLib/SliderPreference/src/com/android/settingslib/widget/SliderPreference.java
@@ -268,7 +268,9 @@
mSlider.setValueFrom(mMin);
mSlider.setValueTo(mMax);
mSlider.setValue(mSliderValue);
+ mSlider.clearOnSliderTouchListeners();
mSlider.addOnSliderTouchListener(mTouchListener);
+ mSlider.clearOnChangeListeners();
mSlider.addOnChangeListener(mChangeListener);
mSlider.setEnabled(isEnabled());
@@ -487,7 +489,7 @@
* set the {@link Slider}'s value to the stored value.
*/
void syncValueInternal(@NonNull Slider slider) {
- int sliderValue = mMin + (int) slider.getValue();
+ int sliderValue = (int) slider.getValue();
if (sliderValue != mSliderValue) {
if (callChangeListener(sliderValue)) {
setValueInternal(sliderValue, false);
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
index fcaedd2..a52222b 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
@@ -64,7 +64,7 @@
bottom = itemPaddingVertical,
)
val itemPaddingAround = 8.dp
- val itemDividerHeight = 32.dp
+ val itemDividerHeight = if (isSpaExpressiveEnabled) 40.dp else 32.dp
val iconLarge = 48.dp
val introIconSize = 40.dp
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetPreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetPreference.kt
index 3aa7ad0..8b1ed93 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetPreference.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetPreference.kt
@@ -28,8 +28,6 @@
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
@@ -104,8 +102,17 @@
@Composable
private fun PreferenceDivider() {
Box(
- Modifier.padding(horizontal = SettingsDimension.itemPaddingEnd)
- .size(width = 1.dp, height = SettingsDimension.itemDividerHeight)
- .background(color = MaterialTheme.colorScheme.divider)
+ if (isSpaExpressiveEnabled) {
+ Modifier.padding(
+ start = SettingsDimension.paddingSmall,
+ end = SettingsDimension.paddingExtraSmall6,
+ )
+ .size(width = 1.dp, height = SettingsDimension.itemDividerHeight)
+ .background(color = MaterialTheme.colorScheme.outline)
+ } else {
+ Modifier.padding(horizontal = SettingsDimension.itemPaddingEnd)
+ .size(width = 1.dp, height = SettingsDimension.itemDividerHeight)
+ .background(color = MaterialTheme.colorScheme.divider)
+ }
)
}
diff --git a/packages/SettingsLib/Spa/testutils/src/com/android/settingslib/spa/testutils/FlowTestUtil.kt b/packages/SettingsLib/Spa/testutils/src/com/android/settingslib/spa/testutils/FlowTestUtil.kt
index 99c6a3f..591dff7 100644
--- a/packages/SettingsLib/Spa/testutils/src/com/android/settingslib/spa/testutils/FlowTestUtil.kt
+++ b/packages/SettingsLib/Spa/testutils/src/com/android/settingslib/spa/testutils/FlowTestUtil.kt
@@ -17,18 +17,50 @@
package com.android.settingslib.spa.testutils
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.withTimeoutOrNull
+/**
+ * Collects the first element emitted by this flow within a given timeout.
+ *
+ * If the flow emits a value within the given timeout, this function returns that value. If the
+ * timeout expires before the flow emits any values, this function returns null.
+ *
+ * This function is similar to [kotlinx.coroutines.flow.firstOrNull], but it adds a timeout to
+ * prevent potentially infinite waiting.
+ *
+ * @param timeMillis The timeout in milliseconds. Defaults to 500 milliseconds.
+ * @return The first element emitted by the flow within the timeout, or null if the timeout expires.
+ */
suspend fun <T> Flow<T>.firstWithTimeoutOrNull(timeMillis: Long = 500): T? =
- withTimeoutOrNull(timeMillis) {
- first()
- }
+ withTimeoutOrNull(timeMillis) { firstOrNull() }
-suspend fun <T> Flow<T>.toListWithTimeout(timeMillis: Long = 500): List<T> {
- val list = mutableListOf<T>()
- return withTimeoutOrNull(timeMillis) {
- toList(list)
- } ?: list
+/**
+ * Collects elements from this flow for a given time and returns the last emitted element, or null
+ * if the flow did not emit any elements.
+ *
+ * This function is useful when you need to retrieve the last value emitted by a flow within a
+ * specific timeframe, but the flow might complete without emitting anything or might not emit a
+ * value within the given timeout.
+ *
+ * @param timeMillis The timeout in milliseconds. Defaults to 500ms.
+ * @return The last emitted element, or null if the flow did not emit any elements.
+ */
+suspend fun <T> Flow<T>.lastWithTimeoutOrNull(timeMillis: Long = 500): T? =
+ toListWithTimeout(timeMillis).lastOrNull()
+
+/**
+ * Collects elements from this flow into a list with a timeout.
+ *
+ * This function attempts to collect all elements from the flow and store them in a list. If the
+ * collection process takes longer than the specified timeout, the collection is canceled and the
+ * function returns the elements collected up to that point.
+ *
+ * @param timeMillis The timeout duration in milliseconds. Defaults to 500 milliseconds.
+ * @return A list containing the collected elements, or an empty list if the timeout was reached
+ * before any elements were collected.
+ */
+suspend fun <T> Flow<T>.toListWithTimeout(timeMillis: Long = 500): List<T> = buildList {
+ withTimeoutOrNull(timeMillis) { toList(this@buildList) }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidAudioRoutingConstants.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidAudioRoutingConstants.java
index ca0cad7..7d91050 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidAudioRoutingConstants.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidAudioRoutingConstants.java
@@ -19,6 +19,7 @@
import android.media.AudioAttributes;
import android.media.AudioDeviceAttributes;
import android.media.AudioDeviceInfo;
+import android.media.MediaRecorder;
import androidx.annotation.IntDef;
@@ -61,15 +62,20 @@
@IntDef({
RoutingValue.AUTO,
RoutingValue.HEARING_DEVICE,
- RoutingValue.DEVICE_SPEAKER,
+ RoutingValue.BUILTIN_DEVICE,
})
public @interface RoutingValue {
int AUTO = 0;
int HEARING_DEVICE = 1;
- int DEVICE_SPEAKER = 2;
+ int BUILTIN_DEVICE = 2;
}
- public static final AudioDeviceAttributes DEVICE_SPEAKER_OUT = new AudioDeviceAttributes(
+ public static final AudioDeviceAttributes BUILTIN_SPEAKER = new AudioDeviceAttributes(
AudioDeviceAttributes.ROLE_OUTPUT, AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, "");
+ public static final AudioDeviceAttributes BUILTIN_MIC = new AudioDeviceAttributes(
+ AudioDeviceAttributes.ROLE_INPUT, AudioDeviceInfo.TYPE_BUILTIN_MIC, "");
+
+ public static final int MICROPHONE_SOURCE_VOICE_COMMUNICATION =
+ MediaRecorder.AudioSource.VOICE_COMMUNICATION;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidAudioRoutingHelper.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidAudioRoutingHelper.java
index 8eaea0e..1f72725 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidAudioRoutingHelper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidAudioRoutingHelper.java
@@ -16,26 +16,34 @@
package com.android.settingslib.bluetooth;
+import static com.android.settingslib.bluetooth.HearingAidAudioRoutingConstants.BUILTIN_MIC;
+import static com.android.settingslib.bluetooth.HearingAidAudioRoutingConstants.MICROPHONE_SOURCE_VOICE_COMMUNICATION;
+
import android.content.Context;
import android.media.AudioAttributes;
import android.media.AudioDeviceAttributes;
import android.media.AudioDeviceInfo;
import android.media.AudioManager;
import android.media.audiopolicy.AudioProductStrategy;
+import android.util.Log;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
+import com.android.settingslib.bluetooth.HearingAidAudioRoutingConstants.RoutingValue;
+
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
- * A helper class to configure the routing strategy for hearing aids.
+ * A helper class to configure the audio routing for hearing aids.
*/
public class HearingAidAudioRoutingHelper {
+ private static final String TAG = "HearingAidAudioRoutingHelper";
+
private final AudioManager mAudioManager;
public HearingAidAudioRoutingHelper(Context context) {
@@ -73,26 +81,26 @@
* @param hearingDevice {@link AudioDeviceAttributes} of the device to be changed in audio
* routing
* @param routingValue one of value defined in
- * {@link HearingAidAudioRoutingConstants.RoutingValue}, denotes routing
+ * {@link RoutingValue}, denotes routing
* destination.
* @return {code true} if the routing value successfully configure
*/
public boolean setPreferredDeviceRoutingStrategies(
List<AudioProductStrategy> supportedStrategies, AudioDeviceAttributes hearingDevice,
- @HearingAidAudioRoutingConstants.RoutingValue int routingValue) {
+ @RoutingValue int routingValue) {
boolean status;
switch (routingValue) {
- case HearingAidAudioRoutingConstants.RoutingValue.AUTO:
+ case RoutingValue.AUTO:
status = removePreferredDeviceForStrategies(supportedStrategies);
return status;
- case HearingAidAudioRoutingConstants.RoutingValue.HEARING_DEVICE:
+ case RoutingValue.HEARING_DEVICE:
status = removePreferredDeviceForStrategies(supportedStrategies);
status &= setPreferredDeviceForStrategies(supportedStrategies, hearingDevice);
return status;
- case HearingAidAudioRoutingConstants.RoutingValue.DEVICE_SPEAKER:
+ case RoutingValue.BUILTIN_DEVICE:
status = removePreferredDeviceForStrategies(supportedStrategies);
status &= setPreferredDeviceForStrategies(supportedStrategies,
- HearingAidAudioRoutingConstants.DEVICE_SPEAKER_OUT);
+ HearingAidAudioRoutingConstants.BUILTIN_SPEAKER);
return status;
default:
throw new IllegalArgumentException("Unexpected routingValue: " + routingValue);
@@ -100,21 +108,76 @@
}
/**
- * Gets the matched hearing device {@link AudioDeviceAttributes} for {@code device}.
+ * Set the preferred input device for calls.
*
- * <p>Will also try to match the {@link CachedBluetoothDevice#getSubDevice()} of {@code device}
+ * <p>Note that hearing device needs to be valid input device to be found in AudioManager.
+ * <p>Routing value can be:
+ * <ul>
+ * <li> {@link RoutingValue#AUTO} - Allow the system to automatically select the appropriate
+ * audio routing for calls.</li>
+ * <li> {@link RoutingValue#HEARING_DEVICE} - Set input device to this hearing device.</li>
+ * <li> {@link RoutingValue#BUILTIN_DEVICE} - Set input device to builtin microphone. </li>
+ * </ul>
+ * @param routingValue The desired routing value for calls
+ * @return {@code true} if the operation was successful
+ */
+ public boolean setPreferredInputDeviceForCalls(@Nullable CachedBluetoothDevice hearingDevice,
+ @RoutingValue int routingValue) {
+ AudioDeviceAttributes hearingDeviceAttributes = getMatchedHearingDeviceAttributesInput(
+ hearingDevice);
+ if (hearingDeviceAttributes == null) {
+ Log.w(TAG, "Can not find expected input AudioDeviceAttributes for hearing device: "
+ + hearingDevice.getDevice().getAnonymizedAddress());
+ return false;
+ }
+
+ final int audioSource = MICROPHONE_SOURCE_VOICE_COMMUNICATION;
+ return switch (routingValue) {
+ case RoutingValue.AUTO ->
+ mAudioManager.clearPreferredDevicesForCapturePreset(audioSource);
+ case RoutingValue.HEARING_DEVICE -> {
+ mAudioManager.clearPreferredDevicesForCapturePreset(audioSource);
+ yield mAudioManager.setPreferredDeviceForCapturePreset(audioSource,
+ hearingDeviceAttributes);
+ }
+ case RoutingValue.BUILTIN_DEVICE -> {
+ mAudioManager.clearPreferredDevicesForCapturePreset(audioSource);
+ yield mAudioManager.setPreferredDeviceForCapturePreset(audioSource, BUILTIN_MIC);
+ }
+ default -> throw new IllegalArgumentException(
+ "Unexpected routingValue: " + routingValue);
+ };
+ }
+
+ /**
+ * Clears the preferred input device for calls.
+ *
+ * {@code true} if the operation was successful
+ */
+ public boolean clearPreferredInputDeviceForCalls() {
+ return mAudioManager.clearPreferredDevicesForCapturePreset(
+ MICROPHONE_SOURCE_VOICE_COMMUNICATION);
+ }
+
+ /**
+ * Gets the matched output hearing device {@link AudioDeviceAttributes} for {@code device}.
+ *
+ * <p>Will also try to match the {@link CachedBluetoothDevice#getSubDevice()} and
+ * {@link CachedBluetoothDevice#getMemberDevice()} of {@code device}
*
* @param device the {@link CachedBluetoothDevice} need to be hearing aid device
* @return the requested AudioDeviceAttributes or {@code null} if not match
*/
@Nullable
- public AudioDeviceAttributes getMatchedHearingDeviceAttributes(CachedBluetoothDevice device) {
+ public AudioDeviceAttributes getMatchedHearingDeviceAttributesForOutput(
+ @Nullable CachedBluetoothDevice device) {
if (device == null || !device.isHearingAidDevice()) {
return null;
}
AudioDeviceInfo[] audioDevices = mAudioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
for (AudioDeviceInfo audioDevice : audioDevices) {
+ //TODO: b/370812132 - Need to update if TYPE_LEA_HEARING_AID is added
// ASHA for TYPE_HEARING_AID, HAP for TYPE_BLE_HEADSET
if (audioDevice.getType() == AudioDeviceInfo.TYPE_HEARING_AID
|| audioDevice.getType() == AudioDeviceInfo.TYPE_BLE_HEADSET) {
@@ -126,6 +189,35 @@
return null;
}
+ /**
+ * Gets the matched input hearing device {@link AudioDeviceAttributes} for {@code device}.
+ *
+ * <p>Will also try to match the {@link CachedBluetoothDevice#getSubDevice()} and
+ * {@link CachedBluetoothDevice#getMemberDevice()} of {@code device}
+ *
+ * @param device the {@link CachedBluetoothDevice} need to be hearing aid device
+ * @return the requested AudioDeviceAttributes or {@code null} if not match
+ */
+ @Nullable
+ private AudioDeviceAttributes getMatchedHearingDeviceAttributesInput(
+ @Nullable CachedBluetoothDevice device) {
+ if (device == null || !device.isHearingAidDevice()) {
+ return null;
+ }
+
+ AudioDeviceInfo[] audioDevices = mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS);
+ for (AudioDeviceInfo audioDevice : audioDevices) {
+ //TODO: b/370812132 - Need to update if TYPE_LEA_HEARING_AID is added
+ // HAP for TYPE_BLE_HEADSET
+ if (audioDevice.getType() == AudioDeviceInfo.TYPE_BLE_HEADSET) {
+ if (matchAddress(device, audioDevice)) {
+ return new AudioDeviceAttributes(audioDevice);
+ }
+ }
+ }
+ return null;
+ }
+
private boolean matchAddress(CachedBluetoothDevice device, AudioDeviceInfo audioDevice) {
final String audioDeviceAddress = audioDevice.getAddress();
final CachedBluetoothDevice subDevice = device.getSubDevice();
@@ -142,7 +234,6 @@
boolean status = true;
for (AudioProductStrategy strategy : strategies) {
status &= mAudioManager.setPreferredDeviceForStrategy(strategy, audioDevice);
-
}
return status;
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java
index 1ca4c2b..ad34e83 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java
@@ -31,6 +31,7 @@
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.settingslib.bluetooth.HearingAidAudioRoutingConstants.RoutingValue;
import java.util.HashSet;
import java.util.List;
@@ -277,13 +278,19 @@
void onActiveDeviceChanged(CachedBluetoothDevice device) {
if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_AUDIO_ROUTING)) {
- if (device.isActiveDevice(BluetoothProfile.HEARING_AID) || device.isActiveDevice(
- BluetoothProfile.LE_AUDIO)) {
+ if (device.isConnectedHearingAidDevice()) {
setAudioRoutingConfig(device);
} else {
clearAudioRoutingConfig();
}
}
+ if (com.android.settingslib.flags.Flags.hearingDevicesInputRoutingControl()) {
+ if (device.isConnectedHearingAidDevice()) {
+ setMicrophoneForCalls(device);
+ } else {
+ clearMicrophoneForCalls();
+ }
+ }
}
void syncDeviceIfNeeded(CachedBluetoothDevice device) {
@@ -311,9 +318,25 @@
HearingDeviceLocalDataManager.clear(mContext, device.getDevice());
}
+ private void setMicrophoneForCalls(CachedBluetoothDevice device) {
+ boolean useRemoteMicrophone = device.getDevice().isMicrophonePreferredForCalls();
+ boolean status = mRoutingHelper.setPreferredInputDeviceForCalls(device,
+ useRemoteMicrophone ? RoutingValue.AUTO : RoutingValue.BUILTIN_DEVICE);
+ if (!status) {
+ Log.d(TAG, "Fail to configure setPreferredInputDeviceForCalls");
+ }
+ }
+
+ private void clearMicrophoneForCalls() {
+ boolean status = mRoutingHelper.clearPreferredInputDeviceForCalls();
+ if (!status) {
+ Log.d(TAG, "Fail to configure clearMicrophoneForCalls");
+ }
+ }
+
private void setAudioRoutingConfig(CachedBluetoothDevice device) {
AudioDeviceAttributes hearingDeviceAttributes =
- mRoutingHelper.getMatchedHearingDeviceAttributes(device);
+ mRoutingHelper.getMatchedHearingDeviceAttributesForOutput(device);
if (hearingDeviceAttributes == null) {
Log.w(TAG, "Can not find expected AudioDeviceAttributes for hearing device: "
+ device.getDevice().getAnonymizedAddress());
@@ -321,17 +344,13 @@
}
final int callRoutingValue = Settings.Secure.getInt(mContentResolver,
- Settings.Secure.HEARING_AID_CALL_ROUTING,
- HearingAidAudioRoutingConstants.RoutingValue.AUTO);
+ Settings.Secure.HEARING_AID_CALL_ROUTING, RoutingValue.AUTO);
final int mediaRoutingValue = Settings.Secure.getInt(mContentResolver,
- Settings.Secure.HEARING_AID_MEDIA_ROUTING,
- HearingAidAudioRoutingConstants.RoutingValue.AUTO);
+ Settings.Secure.HEARING_AID_MEDIA_ROUTING, RoutingValue.AUTO);
final int ringtoneRoutingValue = Settings.Secure.getInt(mContentResolver,
- Settings.Secure.HEARING_AID_RINGTONE_ROUTING,
- HearingAidAudioRoutingConstants.RoutingValue.AUTO);
+ Settings.Secure.HEARING_AID_RINGTONE_ROUTING, RoutingValue.AUTO);
final int systemSoundsRoutingValue = Settings.Secure.getInt(mContentResolver,
- Settings.Secure.HEARING_AID_NOTIFICATION_ROUTING,
- HearingAidAudioRoutingConstants.RoutingValue.AUTO);
+ Settings.Secure.HEARING_AID_NOTIFICATION_ROUTING, RoutingValue.AUTO);
setPreferredDeviceRoutingStrategies(
HearingAidAudioRoutingConstants.CALL_ROUTING_ATTRIBUTES,
@@ -351,21 +370,21 @@
// Don't need to pass hearingDevice when we want to reset it (set to AUTO).
setPreferredDeviceRoutingStrategies(
HearingAidAudioRoutingConstants.CALL_ROUTING_ATTRIBUTES,
- /* hearingDevice = */ null, HearingAidAudioRoutingConstants.RoutingValue.AUTO);
+ /* hearingDevice = */ null, RoutingValue.AUTO);
setPreferredDeviceRoutingStrategies(
HearingAidAudioRoutingConstants.MEDIA_ROUTING_ATTRIBUTES,
- /* hearingDevice = */ null, HearingAidAudioRoutingConstants.RoutingValue.AUTO);
+ /* hearingDevice = */ null, RoutingValue.AUTO);
setPreferredDeviceRoutingStrategies(
HearingAidAudioRoutingConstants.RINGTONE_ROUTING_ATTRIBUTES,
- /* hearingDevice = */ null, HearingAidAudioRoutingConstants.RoutingValue.AUTO);
+ /* hearingDevice = */ null, RoutingValue.AUTO);
setPreferredDeviceRoutingStrategies(
HearingAidAudioRoutingConstants.NOTIFICATION_ROUTING_ATTRIBUTES,
- /* hearingDevice = */ null, HearingAidAudioRoutingConstants.RoutingValue.AUTO);
+ /* hearingDevice = */ null, RoutingValue.AUTO);
}
private void setPreferredDeviceRoutingStrategies(int[] attributeSdkUsageList,
AudioDeviceAttributes hearingDevice,
- @HearingAidAudioRoutingConstants.RoutingValue int routingValue) {
+ @RoutingValue int routingValue) {
final List<AudioProductStrategy> supportedStrategies =
mRoutingHelper.getSupportedStrategies(attributeSdkUsageList);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidAudioRoutingHelperTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidAudioRoutingHelperTest.java
index c835244..dc609bd 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidAudioRoutingHelperTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidAudioRoutingHelperTest.java
@@ -16,9 +16,14 @@
package com.android.settingslib.bluetooth;
+import static com.android.settingslib.bluetooth.HearingAidAudioRoutingConstants.BUILTIN_MIC;
+import static com.android.settingslib.bluetooth.HearingAidAudioRoutingConstants.BUILTIN_SPEAKER;
+import static com.android.settingslib.bluetooth.HearingAidAudioRoutingConstants.MICROPHONE_SOURCE_VOICE_COMMUNICATION;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
@@ -35,10 +40,13 @@
import androidx.test.core.app.ApplicationProvider;
+import com.android.settingslib.bluetooth.HearingAidAudioRoutingConstants.RoutingValue;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnit;
@@ -67,28 +75,35 @@
@Spy
private AudioManager mAudioManager = mContext.getSystemService(AudioManager.class);
@Mock
- private AudioDeviceInfo mAudioDeviceInfo;
+ private AudioDeviceInfo mHearingDeviceInfoOutput;
+ @Mock
+ private AudioDeviceInfo mLeHearingDeviceInfoInput;
@Mock
private CachedBluetoothDevice mCachedBluetoothDevice;
@Mock
private CachedBluetoothDevice mSubCachedBluetoothDevice;
- private AudioDeviceAttributes mHearingDeviceAttribute;
+ private AudioDeviceAttributes mHearingDeviceAttributeOutput;
private HearingAidAudioRoutingHelper mHelper;
@Before
public void setUp() {
doReturn(mAudioManager).when(mContext).getSystemService(AudioManager.class);
- when(mAudioDeviceInfo.getType()).thenReturn(AudioDeviceInfo.TYPE_HEARING_AID);
- when(mAudioDeviceInfo.getAddress()).thenReturn(TEST_DEVICE_ADDRESS);
+ when(mHearingDeviceInfoOutput.getType()).thenReturn(AudioDeviceInfo.TYPE_HEARING_AID);
+ when(mHearingDeviceInfoOutput.getAddress()).thenReturn(TEST_DEVICE_ADDRESS);
+ when(mLeHearingDeviceInfoInput.getType()).thenReturn(AudioDeviceInfo.TYPE_BLE_HEADSET);
+ when(mLeHearingDeviceInfoInput.getAddress()).thenReturn(TEST_DEVICE_ADDRESS);
+ when(mCachedBluetoothDevice.getAddress()).thenReturn(TEST_DEVICE_ADDRESS);
when(mAudioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)).thenReturn(
- new AudioDeviceInfo[]{mAudioDeviceInfo});
+ new AudioDeviceInfo[]{mHearingDeviceInfoOutput});
+ when(mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)).thenReturn(
+ new AudioDeviceInfo[]{mLeHearingDeviceInfoInput});
doReturn(Collections.emptyList()).when(mAudioManager).getPreferredDevicesForStrategy(
any(AudioProductStrategy.class));
when(mAudioStrategy.getAudioAttributesForLegacyStreamType(
AudioManager.STREAM_MUSIC))
.thenReturn((new AudioAttributes.Builder()).build());
- mHearingDeviceAttribute = new AudioDeviceAttributes(
+ mHearingDeviceAttributeOutput = new AudioDeviceAttributes(
AudioDeviceAttributes.ROLE_OUTPUT,
AudioDeviceInfo.TYPE_HEARING_AID,
TEST_DEVICE_ADDRESS);
@@ -99,11 +114,10 @@
@Test
public void setPreferredDeviceRoutingStrategies_hadValueThenValueAuto_callRemoveStrategy() {
when(mAudioManager.getPreferredDeviceForStrategy(mAudioStrategy)).thenReturn(
- mHearingDeviceAttribute);
+ mHearingDeviceAttributeOutput);
mHelper.setPreferredDeviceRoutingStrategies(List.of(mAudioStrategy),
- mHearingDeviceAttribute,
- HearingAidAudioRoutingConstants.RoutingValue.AUTO);
+ mHearingDeviceAttributeOutput, RoutingValue.AUTO);
verify(mAudioManager, atLeastOnce()).removePreferredDeviceForStrategy(mAudioStrategy);
}
@@ -113,8 +127,7 @@
when(mAudioManager.getPreferredDeviceForStrategy(mAudioStrategy)).thenReturn(null);
mHelper.setPreferredDeviceRoutingStrategies(List.of(mAudioStrategy),
- mHearingDeviceAttribute,
- HearingAidAudioRoutingConstants.RoutingValue.AUTO);
+ mHearingDeviceAttributeOutput, RoutingValue.AUTO);
verify(mAudioManager, never()).removePreferredDeviceForStrategy(mAudioStrategy);
}
@@ -122,63 +135,95 @@
@Test
public void setPreferredDeviceRoutingStrategies_valueHearingDevice_callSetStrategy() {
mHelper.setPreferredDeviceRoutingStrategies(List.of(mAudioStrategy),
- mHearingDeviceAttribute,
- HearingAidAudioRoutingConstants.RoutingValue.HEARING_DEVICE);
+ mHearingDeviceAttributeOutput, RoutingValue.HEARING_DEVICE);
verify(mAudioManager, atLeastOnce()).setPreferredDeviceForStrategy(mAudioStrategy,
- mHearingDeviceAttribute);
+ mHearingDeviceAttributeOutput);
}
@Test
- public void setPreferredDeviceRoutingStrategies_valueDeviceSpeaker_callSetStrategy() {
- final AudioDeviceAttributes speakerDevice = new AudioDeviceAttributes(
- AudioDeviceAttributes.ROLE_OUTPUT, AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, "");
+ public void setPreferredDeviceRoutingStrategies_valueBuiltinDevice_callSetStrategy() {
mHelper.setPreferredDeviceRoutingStrategies(List.of(mAudioStrategy),
- mHearingDeviceAttribute,
- HearingAidAudioRoutingConstants.RoutingValue.DEVICE_SPEAKER);
+ mHearingDeviceAttributeOutput, RoutingValue.BUILTIN_DEVICE);
verify(mAudioManager, atLeastOnce()).setPreferredDeviceForStrategy(mAudioStrategy,
- speakerDevice);
+ BUILTIN_SPEAKER);
}
@Test
- public void getMatchedHearingDeviceAttributes_mainHearingDevice_equalAddress() {
+ public void getMatchedHearingDeviceAttributesForOutput_mainHearingDevice_equalAddress() {
when(mCachedBluetoothDevice.isHearingAidDevice()).thenReturn(true);
when(mCachedBluetoothDevice.getAddress()).thenReturn(TEST_DEVICE_ADDRESS);
- final String targetAddress = mHelper.getMatchedHearingDeviceAttributes(
+ final String targetAddress = mHelper.getMatchedHearingDeviceAttributesForOutput(
mCachedBluetoothDevice).getAddress();
- assertThat(targetAddress).isEqualTo(mHearingDeviceAttribute.getAddress());
+ assertThat(targetAddress).isEqualTo(mHearingDeviceAttributeOutput.getAddress());
}
@Test
- public void getMatchedHearingDeviceAttributes_subHearingDevice_equalAddress() {
+ public void getMatchedHearingDeviceAttributesForOutput_subHearingDevice_equalAddress() {
when(mCachedBluetoothDevice.isHearingAidDevice()).thenReturn(true);
when(mCachedBluetoothDevice.getAddress()).thenReturn(NOT_EXPECT_DEVICE_ADDRESS);
when(mCachedBluetoothDevice.getSubDevice()).thenReturn(mSubCachedBluetoothDevice);
when(mSubCachedBluetoothDevice.isHearingAidDevice()).thenReturn(true);
when(mSubCachedBluetoothDevice.getAddress()).thenReturn(TEST_DEVICE_ADDRESS);
- final String targetAddress = mHelper.getMatchedHearingDeviceAttributes(
+ final String targetAddress = mHelper.getMatchedHearingDeviceAttributesForOutput(
mCachedBluetoothDevice).getAddress();
- assertThat(targetAddress).isEqualTo(mHearingDeviceAttribute.getAddress());
+ assertThat(targetAddress).isEqualTo(mHearingDeviceAttributeOutput.getAddress());
}
@Test
- public void getMatchedHearingDeviceAttributes_memberHearingDevice_equalAddress() {
+ public void getMatchedHearingDeviceAttributesForOutput_memberHearingDevice_equalAddress() {
when(mSubCachedBluetoothDevice.isHearingAidDevice()).thenReturn(true);
when(mSubCachedBluetoothDevice.getAddress()).thenReturn(TEST_DEVICE_ADDRESS);
- final Set<CachedBluetoothDevice> memberDevices = new HashSet<CachedBluetoothDevice>();
+ final Set<CachedBluetoothDevice> memberDevices = new HashSet<>();
memberDevices.add(mSubCachedBluetoothDevice);
when(mCachedBluetoothDevice.isHearingAidDevice()).thenReturn(true);
when(mCachedBluetoothDevice.getAddress()).thenReturn(NOT_EXPECT_DEVICE_ADDRESS);
when(mCachedBluetoothDevice.getMemberDevice()).thenReturn(memberDevices);
- final String targetAddress = mHelper.getMatchedHearingDeviceAttributes(
+ final String targetAddress = mHelper.getMatchedHearingDeviceAttributesForOutput(
mCachedBluetoothDevice).getAddress();
- assertThat(targetAddress).isEqualTo(mHearingDeviceAttribute.getAddress());
+ assertThat(targetAddress).isEqualTo(mHearingDeviceAttributeOutput.getAddress());
+ }
+
+ @Test
+ public void setPreferredInputDeviceForCalls_valueAuto_callClearPreset() {
+ when(mCachedBluetoothDevice.isHearingAidDevice()).thenReturn(true);
+
+ mHelper.setPreferredInputDeviceForCalls(mCachedBluetoothDevice, RoutingValue.AUTO);
+
+ verify(mAudioManager).clearPreferredDevicesForCapturePreset(
+ MICROPHONE_SOURCE_VOICE_COMMUNICATION);
+ }
+
+ @Test
+ public void setPreferredInputDeviceForCalls_valueHearingDevice_callSetPresetToHearingDevice() {
+ final ArgumentCaptor<AudioDeviceAttributes> audioDeviceAttributesCaptor =
+ ArgumentCaptor.forClass(AudioDeviceAttributes.class);
+ when(mCachedBluetoothDevice.isHearingAidDevice()).thenReturn(true);
+
+ mHelper.setPreferredInputDeviceForCalls(mCachedBluetoothDevice,
+ RoutingValue.HEARING_DEVICE);
+
+ verify(mAudioManager).setPreferredDeviceForCapturePreset(
+ eq(MICROPHONE_SOURCE_VOICE_COMMUNICATION), audioDeviceAttributesCaptor.capture());
+ assertThat(audioDeviceAttributesCaptor.getValue().getAddress()).isEqualTo(
+ TEST_DEVICE_ADDRESS);
+ }
+
+ @Test
+ public void setPreferredInputDeviceForCalls_valueBuiltinDevice_callClearPresetToBuiltinMic() {
+ when(mCachedBluetoothDevice.isHearingAidDevice()).thenReturn(true);
+
+ mHelper.setPreferredInputDeviceForCalls(mCachedBluetoothDevice,
+ RoutingValue.BUILTIN_DEVICE);
+
+ verify(mAudioManager).setPreferredDeviceForCapturePreset(
+ MICROPHONE_SOURCE_VOICE_COMMUNICATION, BUILTIN_MIC);
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
index eb73eee..2458c5b 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
@@ -729,7 +729,7 @@
@Test
public void onActiveDeviceChanged_connected_callSetStrategies() {
- when(mHelper.getMatchedHearingDeviceAttributes(mCachedDevice1)).thenReturn(
+ when(mHelper.getMatchedHearingDeviceAttributesForOutput(mCachedDevice1)).thenReturn(
mHearingDeviceAttribute);
when(mCachedDevice1.isActiveDevice(BluetoothProfile.HEARING_AID)).thenReturn(true);
doReturn(true).when(mHelper).setPreferredDeviceRoutingStrategies(anyList(),
@@ -743,7 +743,7 @@
@Test
public void onActiveDeviceChanged_disconnected_callSetStrategiesWithAutoValue() {
- when(mHelper.getMatchedHearingDeviceAttributes(mCachedDevice1)).thenReturn(
+ when(mHelper.getMatchedHearingDeviceAttributesForOutput(mCachedDevice1)).thenReturn(
mHearingDeviceAttribute);
when(mCachedDevice1.isActiveDevice(BluetoothProfile.HEARING_AID)).thenReturn(false);
doReturn(true).when(mHelper).setPreferredDeviceRoutingStrategies(anyList(), any(),
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
index 9ab2812..9c53afe 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
@@ -20,6 +20,7 @@
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
+import androidx.compose.ui.draw.blur
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.BlendMode
@@ -43,6 +44,7 @@
import com.android.compose.animation.scene.Swipe
import com.android.compose.animation.scene.observableTransitionState
import com.android.compose.animation.scene.transitions
+import com.android.compose.modifiers.thenIf
import com.android.systemui.communal.shared.model.CommunalBackgroundType
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.communal.shared.model.CommunalTransitionKeys
@@ -158,6 +160,7 @@
transitions = sceneTransitions,
)
}
+ val isUiBlurred by viewModel.isUiBlurred.collectAsStateWithLifecycle()
val detector = remember { CommunalSwipeDetector() }
@@ -174,9 +177,11 @@
onDispose { viewModel.setTransitionState(null) }
}
+ val blurRadius = with(LocalDensity.current) { viewModel.blurRadiusPx.toDp() }
+
SceneTransitionLayout(
state = state,
- modifier = modifier.fillMaxSize(),
+ modifier = modifier.fillMaxSize().thenIf(isUiBlurred) { Modifier.blur(blurRadius) },
swipeSourceDetector = detector,
swipeDetector = detector,
) {
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 58bfd08..46e0efa 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
@@ -284,8 +284,9 @@
viewModel: NotificationsPlaceholderViewModel,
maxScrimTop: () -> Float,
shouldPunchHoleBehindScrim: Boolean,
+ stackTopPadding: Dp,
+ stackBottomPadding: Dp,
shouldFillMaxSize: Boolean = true,
- shouldReserveSpaceForNavBar: Boolean = true,
shouldIncludeHeadsUpSpace: Boolean = true,
shouldShowScrim: Boolean = true,
supportNestedScrolling: Boolean,
@@ -307,10 +308,7 @@
val expansionFraction by viewModel.expandFraction.collectAsStateWithLifecycle(0f)
val shadeToQsFraction by viewModel.shadeToQsFraction.collectAsStateWithLifecycle(0f)
- val topPadding = dimensionResource(id = R.dimen.notification_side_paddings)
val navBarHeight = WindowInsets.systemBars.asPaddingValues().calculateBottomPadding()
- val bottomPadding = if (shouldReserveSpaceForNavBar) navBarHeight else 0.dp
-
val screenHeight = with(density) { LocalConfiguration.current.screenHeightDp.dp.toPx() }
/**
@@ -363,8 +361,9 @@
val shadeScrollState by remember {
derivedStateOf {
ShadeScrollState(
- // we are not scrolled to the top unless the scrim is at its maximum offset.
- isScrolledToTop = scrimOffset.value >= 0f,
+ // we are not scrolled to the top unless the scroll position is zero,
+ // and the scrim is at its maximum offset
+ isScrolledToTop = scrimOffset.value >= 0f && scrollState.value == 0,
scrollPosition = scrollState.value,
maxScrollPosition = scrollState.maxValue,
)
@@ -574,7 +573,7 @@
}
.stackVerticalOverscroll(coroutineScope) { scrollState.canScrollForward }
.verticalScroll(scrollState)
- .padding(top = topPadding)
+ .padding(top = stackTopPadding, bottom = stackBottomPadding)
.fillMaxWidth()
.onGloballyPositioned { coordinates ->
stackBoundsOnScreen.value = coordinates.boundsInWindow()
@@ -587,11 +586,10 @@
!shouldUseLockscreenStackBounds(layoutState.transitionState)
},
modifier =
- Modifier.notificationStackHeight(
- view = stackScrollView,
- totalVerticalPadding = topPadding + bottomPadding,
- )
- .onSizeChanged { size -> stackHeight.intValue = size.height },
+ Modifier.notificationStackHeight(view = stackScrollView).onSizeChanged {
+ size ->
+ stackHeight.intValue = size.height
+ },
)
Spacer(
modifier =
@@ -607,7 +605,7 @@
stackScrollView = stackScrollView,
viewModel = viewModel,
useHunBounds = { !shouldUseLockscreenHunBounds(layoutState.transitionState) },
- modifier = Modifier.padding(top = topPadding),
+ modifier = Modifier.padding(top = stackTopPadding),
)
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt
index 5790c4a..9eb1f68 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt
@@ -16,12 +16,15 @@
package com.android.systemui.notifications.ui.composable
+import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.layoutId
+import androidx.compose.ui.res.dimensionResource
import com.android.compose.animation.scene.ContentScope
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.UserAction
@@ -34,6 +37,7 @@
import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeOverlayActionsViewModel
import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeOverlayContentViewModel
+import com.android.systemui.res.R
import com.android.systemui.scene.session.ui.composable.SaveableSession
import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.ui.composable.Overlay
@@ -61,7 +65,6 @@
private val clockSection: DefaultClockSection,
private val clockInteractor: KeyguardClockInteractor,
) : Overlay {
-
override val key = Overlays.NotificationsShade
private val actionsViewModel: NotificationsShadeOverlayActionsViewModel by lazy {
@@ -76,6 +79,9 @@
@Composable
override fun ContentScope.Content(modifier: Modifier) {
+
+ val notificationStackPadding = dimensionResource(id = R.dimen.notification_side_paddings)
+
val viewModel =
rememberViewModel("NotificationsShadeOverlay-viewModel") {
contentViewModelFactory.create()
@@ -90,47 +96,52 @@
modifier = modifier,
onScrimClicked = viewModel::onScrimClicked,
) {
- Column {
- if (viewModel.showHeader) {
- val burnIn = rememberBurnIn(clockInteractor)
+ Box {
+ Column {
+ if (viewModel.showHeader) {
+ val burnIn = rememberBurnIn(clockInteractor)
- CollapsedShadeHeader(
- viewModelFactory = viewModel.shadeHeaderViewModelFactory,
- createTintedIconManager = tintedIconManagerFactory::create,
- createBatteryMeterViewController =
- batteryMeterViewControllerFactory::create,
- statusBarIconController = statusBarIconController,
- modifier =
- Modifier.element(NotificationsShade.Elements.StatusBar)
- .layoutId(SingleShadeMeasurePolicy.LayoutId.ShadeHeader),
- )
-
- with(clockSection) {
- SmallClock(
- burnInParams = burnIn.parameters,
- onTopChanged = burnIn.onSmallClockTopChanged,
- modifier = Modifier.fillMaxWidth(),
+ CollapsedShadeHeader(
+ viewModelFactory = viewModel.shadeHeaderViewModelFactory,
+ createTintedIconManager = tintedIconManagerFactory::create,
+ createBatteryMeterViewController =
+ batteryMeterViewControllerFactory::create,
+ statusBarIconController = statusBarIconController,
+ modifier =
+ Modifier.element(NotificationsShade.Elements.StatusBar)
+ .layoutId(SingleShadeMeasurePolicy.LayoutId.ShadeHeader),
)
+
+ with(clockSection) {
+ SmallClock(
+ burnInParams = burnIn.parameters,
+ onTopChanged = burnIn.onSmallClockTopChanged,
+ modifier = Modifier.fillMaxWidth(),
+ )
+ }
}
+
+ NotificationScrollingStack(
+ shadeSession = shadeSession,
+ stackScrollView = stackScrollView.get(),
+ viewModel = placeholderViewModel,
+ maxScrimTop = { 0f },
+ stackTopPadding = notificationStackPadding,
+ stackBottomPadding = notificationStackPadding,
+ shouldPunchHoleBehindScrim = false,
+ shouldFillMaxSize = false,
+ shouldShowScrim = false,
+ supportNestedScrolling = false,
+ modifier = Modifier.fillMaxWidth(),
+ )
}
-
- NotificationScrollingStack(
- shadeSession = shadeSession,
- stackScrollView = stackScrollView.get(),
- viewModel = placeholderViewModel,
- maxScrimTop = { 0f },
- shouldPunchHoleBehindScrim = false,
- shouldFillMaxSize = false,
- shouldReserveSpaceForNavBar = false,
- shouldShowScrim = false,
- supportNestedScrolling = false,
- modifier = Modifier.fillMaxWidth(),
- )
-
// Communicates the bottom position of the drawable area within the shade to NSSL.
NotificationStackCutoffGuideline(
stackScrollView = stackScrollView.get(),
viewModel = placeholderViewModel,
+ modifier =
+ Modifier.align(Alignment.BottomCenter)
+ .padding(bottom = notificationStackPadding),
)
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
index 52adaf2..26cf706 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
@@ -74,7 +74,6 @@
import com.android.compose.animation.scene.animateSceneDpAsState
import com.android.compose.animation.scene.animateSceneFloatAsState
import com.android.compose.animation.scene.content.state.TransitionState
-import com.android.compose.modifiers.padding
import com.android.compose.modifiers.thenIf
import com.android.compose.windowsizeclass.LocalWindowSizeClass
import com.android.systemui.battery.BatteryMeterViewController
@@ -433,12 +432,15 @@
// A 1 pixel is added to compensate for any kind of rounding errors to make sure 100% that
// the notification stack is entirely "below" the entire screen.
val minNotificationStackTop = screenHeight.roundToInt() + 1
+ val notificationStackPadding = dimensionResource(id = R.dimen.notification_side_paddings)
NotificationScrollingStack(
shadeSession = shadeSession,
stackScrollView = notificationStackScrollView,
viewModel = notificationsPlaceholderViewModel,
maxScrimTop = { minNotificationStackTop.toFloat() },
shouldPunchHoleBehindScrim = shouldPunchHoleBehindScrim,
+ stackTopPadding = notificationStackPadding,
+ stackBottomPadding = navBarBottomHeight,
shouldIncludeHeadsUpSpace = false,
supportNestedScrolling = true,
modifier =
@@ -453,7 +455,12 @@
Modifier.align(Alignment.BottomCenter)
.navigationBarsPadding()
.offset { IntOffset(x = 0, y = minNotificationStackTop) }
- .padding(horizontal = shadeHorizontalPadding),
+ .padding(
+ start = shadeHorizontalPadding,
+ top = 0.dp,
+ end = shadeHorizontalPadding,
+ bottom = navBarBottomHeight,
+ ),
)
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt
index 3327fc0..f052e60 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt
@@ -103,12 +103,15 @@
onScrimClicked = viewModel::onScrimClicked,
) {
Column {
- CollapsedShadeHeader(
- viewModelFactory = viewModel.shadeHeaderViewModelFactory,
- createTintedIconManager = tintedIconManagerFactory::create,
- createBatteryMeterViewController = batteryMeterViewControllerFactory::create,
- statusBarIconController = statusBarIconController,
- )
+ if (viewModel.showHeader) {
+ CollapsedShadeHeader(
+ viewModelFactory = viewModel.shadeHeaderViewModelFactory,
+ createTintedIconManager = tintedIconManagerFactory::create,
+ createBatteryMeterViewController =
+ batteryMeterViewControllerFactory::create,
+ statusBarIconController = statusBarIconController,
+ )
+ }
ShadeBody(viewModel = viewModel.quickSettingsContainerViewModel)
}
@@ -178,8 +181,8 @@
Column(
verticalArrangement = Arrangement.spacedBy(QuickSettingsShade.Dimensions.Padding),
horizontalAlignment = Alignment.CenterHorizontally,
- modifier = modifier
- .padding(
+ modifier =
+ modifier.padding(
start = QuickSettingsShade.Dimensions.Padding,
end = QuickSettingsShade.Dimensions.Padding,
bottom = QuickSettingsShade.Dimensions.Padding,
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 aefe83b..0d3bab2 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
@@ -36,7 +36,6 @@
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBars
-import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.overscroll
@@ -76,7 +75,6 @@
import com.android.systemui.battery.BatteryMeterViewController
import com.android.systemui.common.ui.compose.windowinsets.CutoutLocation
import com.android.systemui.common.ui.compose.windowinsets.LocalDisplayCutout
-import com.android.systemui.common.ui.compose.windowinsets.LocalScreenCornerRadius
import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.lifecycle.ExclusiveActivatable
@@ -283,7 +281,7 @@
key = MediaLandscapeTopOffset,
canOverflow = false,
)
-
+ val notificationStackPadding = dimensionResource(id = R.dimen.notification_side_paddings)
val navBarHeight = WindowInsets.systemBars.asPaddingValues().calculateBottomPadding()
val mediaOffsetProvider = remember {
@@ -383,6 +381,8 @@
viewModel = notificationsPlaceholderViewModel,
maxScrimTop = { maxNotifScrimTop.toFloat() },
shouldPunchHoleBehindScrim = shouldPunchHoleBehindScrim,
+ stackTopPadding = notificationStackPadding,
+ stackBottomPadding = navBarHeight,
supportNestedScrolling = true,
onEmptySpaceClick =
viewModel::onEmptySpaceClicked.takeIf { isEmptySpaceClickable },
@@ -422,8 +422,6 @@
modifier: Modifier = Modifier,
shadeSession: SaveableSession,
) {
- val screenCornerRadius = LocalScreenCornerRadius.current
-
val isCustomizing by viewModel.qsSceneAdapter.isCustomizing.collectAsStateWithLifecycle()
val isQsEnabled by viewModel.isQsEnabled.collectAsStateWithLifecycle()
val isCustomizerShowing by
@@ -444,6 +442,7 @@
val unfoldTranslationXForEndSide by
viewModel.unfoldTranslationX(isOnStartSide = false).collectAsStateWithLifecycle(0f)
+ val notificationStackPadding = dimensionResource(id = R.dimen.notification_side_paddings)
val navBarBottomHeight = WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding()
val bottomPadding by
animateDpAsState(
@@ -604,8 +603,9 @@
stackScrollView = notificationStackScrollView,
viewModel = notificationsPlaceholderViewModel,
maxScrimTop = { 0f },
+ stackTopPadding = notificationStackPadding,
+ stackBottomPadding = notificationStackPadding,
shouldPunchHoleBehindScrim = false,
- shouldReserveSpaceForNavBar = false,
supportNestedScrolling = false,
onEmptySpaceClick =
viewModel::onEmptySpaceClicked.takeIf { isEmptySpaceClickable },
@@ -624,7 +624,9 @@
NotificationStackCutoffGuideline(
stackScrollView = notificationStackScrollView,
viewModel = notificationsPlaceholderViewModel,
- modifier = Modifier.align(Alignment.BottomCenter).navigationBarsPadding(),
+ modifier =
+ Modifier.align(Alignment.BottomCenter)
+ .padding(bottom = notificationStackPadding + navBarBottomHeight),
)
}
}
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 8777ff9..495fdaf 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 @@
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 16b4322..8865a07 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.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 @@
/** 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 @@
// 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 @@
.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 @@
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 @@
*/
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 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 @@
_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 @@
// 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 @@
}
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 @@
fun update(
layoutImpl: SceneTransitionLayoutImpl,
- currentTransitionStates: List<TransitionState>,
+ currentTransitionStates: List<List<TransitionState>>,
content: Content,
key: ElementKey,
) {
@@ -326,7 +361,7 @@
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 @@
): 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 @@
// 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 @@
element,
transition,
contentValue = { it.targetOffset },
- transformation = { it.offset },
+ transformation = { it?.offset },
currentValue = { currentOffset },
isSpecified = { it != Offset.Unspecified },
::lerp,
@@ -592,8 +632,7 @@
}
}
- pruneForContent(stateInContent.content)
- layoutImpl.ancestorContentKeys.forEach { content -> pruneForContent(content) }
+ stateInContent.contents.fastForEach { pruneForContent(it) }
}
}
}
@@ -602,9 +641,10 @@
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 @@
}
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 @@
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 @@
}
/**
- * 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 @@
// 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 @@
if (
content != transition.fromContent &&
content != transition.toContent &&
- (!isReplacingOverlay || content != transition.currentScene)
+ (!isReplacingOverlay || content != transition.currentScene) &&
+ transitionDoesNotInvolveAncestorContent(layoutImpl, transition)
) {
return false
}
@@ -938,6 +1004,15 @@
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 @@
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 @@
element,
transition,
contentValue = { 1f },
- transformation = { it.alpha },
+ transformation = { it?.alpha },
currentValue = { 1f },
isSpecified = { true },
::lerp,
@@ -1060,7 +1135,7 @@
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 @@
)
},
)
-
return measurable.measure(
Constraints.fixed(
interruptedSize.width.coerceAtLeast(0),
@@ -1117,7 +1191,7 @@
element,
transition,
contentValue = { Scale.Default },
- transformation = { it.drawScale },
+ transformation = { it?.drawScale },
currentValue = { Scale.Default },
isSpecified = { true },
::lerp,
@@ -1205,7 +1279,8 @@
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,10 +1305,9 @@
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.
+ // The element is shared: interpolate between the value in fromContent and toContent.
// TODO(b/290184746): Support non linear shared paths as well as a way to make sure that shared
// elements follow the finger direction.
val isSharedElement = fromState != null && toState != null
@@ -1265,127 +1339,55 @@
}
}
- // 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
- }
- )
-
// 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
+ // fromContent or toContent during interruptions or when a ancestor transition is running.
+ val transformationContentKey: ContentKey =
+ getTransformationContentKey(
+ isDisabledSharedElement = isSharedElement,
+ currentContent = currentContent,
+ layoutImpl = layoutImpl,
+ transition = transition,
+ element = element,
+ currentSceneState = currentSceneState,
+ )
+ // Get the transformed value, i.e. the target value at the beginning (for entering elements) or
+ // end (for leaving elements) of the transition.
+ val targetState: Element.State = element.stateByContent.getValue(transformationContentKey)
+ val idleValue = contentValue(targetState)
val transformationWithRange =
- transformation(transition.transformationSpec.transformations(element.key, content))
+ transformation(
+ transition.transformationSpec.transformations(element.key, transformationContentKey)
+ )
+
+ val isElementEntering =
+ when {
+ transformationContentKey == toContent -> true
+ transformationContentKey == fromContent -> false
+ isAncestorTransition(layoutImpl, transition) ->
+ isEnteringAncestorTransition(layoutImpl, transition)
+ transformationContentKey == transition.currentScene -> toState == null
+ else -> transformationContentKey == toContent
+ }
val previewTransformation =
transition.previewTransformationSpec?.let {
- transformation(it.transformations(element.key, content))
+ transformation(it.transformations(element.key, transformationContentKey))
}
+
if (previewTransformation != null) {
- val isInPreviewStage = transition.isInPreviewStage
-
- val idleValue = contentValue(contentState)
- val isEntering = content == toContent
- val previewTargetValue =
- with(
- previewTransformation.transformation.requireInterpolatedTransformation(
- element,
- transition,
- ) {
- "Custom transformations in preview specs should not be possible"
- }
- ) {
- layoutImpl.propertyTransformationScope.transform(
- content,
- element.key,
- transition,
- idleValue,
- )
- }
-
- val targetValueOrNull =
- transformationWithRange?.let { transformation ->
- with(
- transformation.transformation.requireInterpolatedTransformation(
- element,
- transition,
- ) {
- "Custom transformations are not allowed for properties with a preview"
- }
- ) {
- layoutImpl.propertyTransformationScope.transform(
- content,
- element.key,
- transition,
- idleValue,
- )
- }
- }
-
- // Make sure we don't read progress if values are the same and we don't need to interpolate,
- // so we don't invalidate the phase where this is read.
- when {
- isInPreviewStage && isEntering && previewTargetValue == targetValueOrNull ->
- return previewTargetValue
- isInPreviewStage && !isEntering && idleValue == previewTargetValue -> return idleValue
- previewTargetValue == targetValueOrNull && idleValue == previewTargetValue ->
- return idleValue
- else -> {}
- }
-
- val previewProgress = transition.previewProgress
- // progress is not needed for all cases of the below when block, therefore read it lazily
- // TODO(b/290184746): Make sure that we don't overflow transformations associated to a range
- val previewRangeProgress =
- previewTransformation.range?.progress(previewProgress) ?: previewProgress
-
- if (isInPreviewStage) {
- // if we're in the preview stage of the transition, interpolate between start state and
- // preview target state:
- return if (isEntering) {
- // i.e. in the entering case between previewTargetValue and targetValue (or
- // idleValue if no transformation is defined in the second stage transition)...
- lerp(previewTargetValue, targetValueOrNull ?: idleValue, previewRangeProgress)
- } else {
- // ...and in the exiting case between the idleValue and the previewTargetValue.
- lerp(idleValue, previewTargetValue, previewRangeProgress)
- }
- }
-
- // if we're in the second stage of the transition, interpolate between the state the
- // element was left at the end of the preview-phase and the target state:
- return if (isEntering) {
- // i.e. in the entering case between preview-end-state and the idleValue...
- lerp(
- lerp(previewTargetValue, targetValueOrNull ?: idleValue, previewRangeProgress),
- idleValue,
- transformationWithRange?.range?.progress(transition.progress) ?: transition.progress,
- )
- } else {
- if (targetValueOrNull == null) {
- // ... and in the exiting case, the element should remain in the preview-end-state
- // if no further transformation is defined in the second-stage transition...
- lerp(idleValue, previewTargetValue, previewRangeProgress)
- } else {
- // ...and otherwise it should be interpolated between preview-end-state and
- // targetValue
- lerp(
- lerp(idleValue, previewTargetValue, previewRangeProgress),
- targetValueOrNull,
- transformationWithRange.range?.progress(transition.progress)
- ?: transition.progress,
- )
- }
- }
+ return computePreviewTransformationValue(
+ transition,
+ idleValue,
+ transformationContentKey,
+ isElementEntering,
+ previewTransformation,
+ element,
+ layoutImpl,
+ transformationWithRange,
+ lerp,
+ )
}
if (transformationWithRange == null) {
@@ -1400,7 +1402,7 @@
is CustomPropertyTransformation ->
return with(transformation) {
layoutImpl.propertyTransformationScope.transform(
- content,
+ transformationContentKey,
element.key,
transition,
transition.coroutineScope,
@@ -1411,11 +1413,10 @@
}
}
- val idleValue = contentValue(contentState)
val targetValue =
with(transformation) {
layoutImpl.propertyTransformationScope.transform(
- content,
+ transformationContentKey,
element.key,
transition,
idleValue,
@@ -1432,21 +1433,183 @@
// TODO(b/290184746): Make sure that we don't overflow transformations associated to a range.
val rangeProgress = transformationWithRange.range?.progress(progress) ?: progress
- // Interpolate between the value at rest and the value before entering/after leaving.
- val isEntering =
- when {
- content == toContent -> true
- content == fromContent -> false
- content == transition.currentScene -> toState == null
- else -> content == toContent
- }
- return if (isEntering) {
+ return if (isElementEntering) {
lerp(targetValue, idleValue, rangeProgress)
} else {
lerp(idleValue, targetValue, rangeProgress)
}
}
+private fun getTransformationContentKey(
+ isDisabledSharedElement: Boolean,
+ currentContent: ContentKey,
+ layoutImpl: SceneTransitionLayoutImpl,
+ transition: TransitionState.Transition,
+ element: Element,
+ currentSceneState: Element.State?,
+): ContentKey {
+ return when {
+ isDisabledSharedElement -> {
+ currentContent
+ }
+ isAncestorTransition(layoutImpl, transition) -> {
+ if (
+ element.stateByContent[transition.fromContent] != null &&
+ transition.transformationSpec.hasTransformation(
+ element.key,
+ transition.fromContent,
+ )
+ ) {
+ transition.fromContent
+ } else if (
+ element.stateByContent[transition.toContent] != null &&
+ transition.transformationSpec.hasTransformation(
+ element.key,
+ transition.toContent,
+ )
+ ) {
+ transition.toContent
+ } 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."
+ )
+ }
+ }
+ currentSceneState != null && currentContent == transition.currentScene -> {
+ currentContent
+ }
+ element.stateByContent[transition.fromContent] != null -> {
+ transition.fromContent
+ }
+ else -> {
+ transition.toContent
+ }
+ }
+}
+
+private inline fun <T> computePreviewTransformationValue(
+ transition: TransitionState.Transition,
+ idleValue: T,
+ transformationContentKey: ContentKey,
+ isEntering: Boolean,
+ previewTransformation: TransformationWithRange<PropertyTransformation<T>>,
+ element: Element,
+ layoutImpl: SceneTransitionLayoutImpl,
+ transformationWithRange: TransformationWithRange<PropertyTransformation<T>>?,
+ lerp: (T, T, Float) -> T,
+): T {
+ val isInPreviewStage = transition.isInPreviewStage
+
+ val previewTargetValue =
+ with(
+ previewTransformation.transformation.requireInterpolatedTransformation(
+ element,
+ transition,
+ ) {
+ "Custom transformations in preview specs should not be possible"
+ }
+ ) {
+ layoutImpl.propertyTransformationScope.transform(
+ transformationContentKey,
+ element.key,
+ transition,
+ idleValue,
+ )
+ }
+
+ val targetValueOrNull =
+ transformationWithRange?.let { transformation ->
+ with(
+ transformation.transformation.requireInterpolatedTransformation(
+ element,
+ transition,
+ ) {
+ "Custom transformations are not allowed for properties with a preview"
+ }
+ ) {
+ layoutImpl.propertyTransformationScope.transform(
+ transformationContentKey,
+ element.key,
+ transition,
+ idleValue,
+ )
+ }
+ }
+
+ // Make sure we don't read progress if values are the same and we don't need to interpolate,
+ // so we don't invalidate the phase where this is read.
+ when {
+ isInPreviewStage && isEntering && previewTargetValue == targetValueOrNull ->
+ return previewTargetValue
+ isInPreviewStage && !isEntering && idleValue == previewTargetValue -> return idleValue
+ previewTargetValue == targetValueOrNull && idleValue == previewTargetValue ->
+ return idleValue
+ else -> {}
+ }
+
+ val previewProgress = transition.previewProgress
+ // progress is not needed for all cases of the below when block, therefore read it lazily
+ // TODO(b/290184746): Make sure that we don't overflow transformations associated to a range
+ val previewRangeProgress =
+ previewTransformation.range?.progress(previewProgress) ?: previewProgress
+
+ if (isInPreviewStage) {
+ // if we're in the preview stage of the transition, interpolate between start state and
+ // preview target state:
+ return if (isEntering) {
+ // i.e. in the entering case between previewTargetValue and targetValue (or
+ // idleValue if no transformation is defined in the second stage transition)...
+ lerp(previewTargetValue, targetValueOrNull ?: idleValue, previewRangeProgress)
+ } else {
+ // ...and in the exiting case between the idleValue and the previewTargetValue.
+ lerp(idleValue, previewTargetValue, previewRangeProgress)
+ }
+ }
+
+ // if we're in the second stage of the transition, interpolate between the state the
+ // element was left at the end of the preview-phase and the target state:
+ return if (isEntering) {
+ // i.e. in the entering case between preview-end-state and the idleValue...
+ lerp(
+ lerp(previewTargetValue, targetValueOrNull ?: idleValue, previewRangeProgress),
+ idleValue,
+ transformationWithRange?.range?.progress(transition.progress) ?: transition.progress,
+ )
+ } else {
+ if (targetValueOrNull == null) {
+ // ... and in the exiting case, the element should remain in the preview-end-state
+ // if no further transformation is defined in the second-stage transition...
+ lerp(idleValue, previewTargetValue, previewRangeProgress)
+ } else {
+ // ...and otherwise it should be interpolated between preview-end-state and
+ // targetValue
+ lerp(
+ lerp(idleValue, previewTargetValue, previewRangeProgress),
+ targetValueOrNull,
+ transformationWithRange.range?.progress(transition.progress) ?: transition.progress,
+ )
+ }
+ }
+}
+
+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 388456e..c10a485 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 @@
// 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 @@
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 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 @@
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 ce385ab..7b30a2a 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 @@
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 @@
builder = builder,
animationScope = animationScope,
elements = sharedElementMap,
- ancestorContentKeys = ancestorContentKeys,
+ ancestors = ancestors,
lookaheadScope = lookaheadScope,
)
.also { onLayoutImpl?.invoke(it) }
@@ -738,9 +738,9 @@
"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 e1cecc7..e5bdc92 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 @@
/** 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 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 6479e69..756d71c 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 @@
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 @@
}
}
- 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 9de297f..ed3a5ca 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 @@
): 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 3716df5..8c5a727 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.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 @@
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/transformation/NestedElementTransformationTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/NestedElementTransformationTest.kt
new file mode 100644
index 0000000..0da422b
--- /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 c6ef8cf..d8b7136 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 @@
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 @@
// 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 @@
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 47c10f5..0dd08d9 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 @@
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 @@
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 @@
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 @@
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 124b61e..bc160fc 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.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 @@
* 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 @@
}
@TransitionTestDsl
-interface TransitionTestAssertionScope {
+interface TransitionTestAssertionScope : CoroutineScope {
/**
* Assert on [element].
*
@@ -312,6 +316,20 @@
)
}
+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 @@
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 @@
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 @@
}
}
- 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/communal/view/viewmodel/CommunalViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
index d70af28..b70f46c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
@@ -25,10 +25,12 @@
import android.widget.RemoteViews
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.systemui.Flags.FLAG_BOUNCER_UI_REVAMP
import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
import com.android.systemui.Flags.FLAG_COMMUNAL_RESPONSIVE_GRID
import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_DIRECT_EDIT_MODE
import com.android.systemui.SysuiTestCase
+import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
import com.android.systemui.communal.data.model.CommunalSmartspaceTimer
import com.android.systemui.communal.data.repository.FakeCommunalMediaRepository
import com.android.systemui.communal.data.repository.FakeCommunalSceneRepository
@@ -69,6 +71,7 @@
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.keyguard.ui.transitions.blurConfig
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.log.logcatLogBuffer
@@ -184,6 +187,7 @@
logcatLogBuffer("CommunalViewModelTest"),
metricsLogger,
kosmos.mediaCarouselController,
+ kosmos.blurConfig,
)
}
@@ -893,6 +897,20 @@
assertThat(selectedKey2).isEqualTo(key)
}
+ @Test
+ @EnableFlags(FLAG_BOUNCER_UI_REVAMP)
+ fun uiIsBlurred_whenPrimaryBouncerIsShowing() =
+ testScope.runTest {
+ val viewModel = createViewModel()
+ val isUiBlurred by collectLastValue(viewModel.isUiBlurred)
+
+ kosmos.fakeKeyguardBouncerRepository.setPrimaryShow(true)
+ assertThat(isUiBlurred).isTrue()
+
+ kosmos.fakeKeyguardBouncerRepository.setPrimaryShow(false)
+ assertThat(isUiBlurred).isFalse()
+ }
+
private suspend fun setIsMainUser(isMainUser: Boolean) {
val user = if (isMainUser) MAIN_USER_INFO else SECONDARY_USER_INFO
with(userRepository) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractorTest.kt
index 28ac169..16f02c5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractorTest.kt
@@ -27,7 +27,6 @@
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.deviceentry.domain.interactor.deviceEntryFaceAuthInteractor
import com.android.systemui.deviceentry.shared.FaceAuthUiEvent
-import com.android.systemui.flags.Flags
import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
@@ -93,9 +92,7 @@
testScope.runTest {
val isEnabled = collectLastValue(underTest.isLongPressHandlingEnabled)
KeyguardState.values().forEach { keyguardState ->
- setUpState(
- keyguardState = keyguardState,
- )
+ setUpState(keyguardState = keyguardState)
if (keyguardState == KeyguardState.LOCKSCREEN) {
assertThat(isEnabled()).isTrue()
@@ -110,10 +107,7 @@
testScope.runTest {
val isEnabled = collectLastValue(underTest.isLongPressHandlingEnabled)
KeyguardState.values().forEach { keyguardState ->
- setUpState(
- keyguardState = keyguardState,
- isQuickSettingsVisible = true,
- )
+ setUpState(keyguardState = keyguardState, isQuickSettingsVisible = true)
assertThat(isEnabled()).isFalse()
}
@@ -290,22 +284,19 @@
keyguardTransitionRepository.sendTransitionSteps(
from = KeyguardState.LOCKSCREEN,
to = KeyguardState.GONE,
- testScope
+ testScope,
)
assertThat(isMenuVisible).isFalse()
keyguardTransitionRepository.sendTransitionSteps(
from = KeyguardState.GONE,
to = KeyguardState.LOCKSCREEN,
- testScope
+ testScope,
)
assertThat(isMenuVisible).isFalse()
}
- private suspend fun createUnderTest(
- isLongPressFeatureEnabled: Boolean = true,
- isRevampedWppFeatureEnabled: Boolean = true,
- ) {
+ private suspend fun createUnderTest(isRevampedWppFeatureEnabled: Boolean = true) {
// This needs to be re-created for each test outside of kosmos since the flag values are
// read during initialization to set up flows. Maybe there is a better way to handle that.
underTest =
@@ -315,10 +306,7 @@
transitionInteractor = kosmos.keyguardTransitionInteractor,
repository = keyguardRepository,
logger = logger,
- featureFlags =
- kosmos.fakeFeatureFlagsClassic.apply {
- set(Flags.LOCK_SCREEN_LONG_PRESS_ENABLED, isLongPressFeatureEnabled)
- },
+ featureFlags = kosmos.fakeFeatureFlagsClassic,
broadcastDispatcher = fakeBroadcastDispatcher,
accessibilityManager = kosmos.accessibilityManagerWrapper,
pulsingGestureListener = kosmos.pulsingGestureListener,
@@ -334,7 +322,7 @@
keyguardTransitionRepository.sendTransitionSteps(
from = KeyguardState.AOD,
to = keyguardState,
- testScope = testScope
+ testScope = testScope,
)
keyguardRepository.setQuickSettingsVisible(isVisible = isQuickSettingsVisible)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt
index 6f7e9d3..e4eb55b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt
@@ -26,9 +26,7 @@
import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.DisableSceneContainer
-import com.android.systemui.flags.Flags
import com.android.systemui.flags.andSceneContainer
-import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.fakeKeyguardClockRepository
import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository
import com.android.systemui.keyguard.shared.model.ClockSize
@@ -77,7 +75,6 @@
@Before
fun setup() {
with(kosmos) {
- fakeFeatureFlagsClassic.set(Flags.LOCK_SCREEN_LONG_PRESS_ENABLED, true)
shadeRepository.setShadeLayoutWide(false)
underTest = lockscreenContentViewModel
underTest.activateIn(testScope)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/pipeline/Media3ActionFactoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/pipeline/Media3ActionFactoryTest.kt
index f8f6fe2..466c9f9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/pipeline/Media3ActionFactoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/pipeline/Media3ActionFactoryTest.kt
@@ -26,6 +26,7 @@
import androidx.media3.session.CommandButton
import androidx.media3.session.MediaController as Media3Controller
import androidx.media3.session.SessionCommand
+import androidx.media3.session.SessionResult
import androidx.media3.session.SessionToken
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -41,6 +42,8 @@
import com.android.systemui.util.concurrency.execution
import com.google.common.collect.ImmutableList
import com.google.common.truth.Truth.assertThat
+import com.google.common.util.concurrent.ListenableFuture
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -60,6 +63,7 @@
private const val CUSTOM_ACTION_NAME = "Custom Action"
private const val CUSTOM_ACTION_COMMAND = "custom-action"
+@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWithLooper
@RunWith(AndroidJUnit4::class)
@@ -84,12 +88,14 @@
}
}
private val customLayout = ImmutableList.of<CommandButton>()
+ private val customCommandFuture = mock<ListenableFuture<SessionResult>>()
private val media3Controller =
mock<Media3Controller> {
on { customLayout } doReturn customLayout
on { sessionExtras } doReturn Bundle()
on { isCommandAvailable(any()) } doReturn true
on { isSessionCommandAvailable(any<SessionCommand>()) } doReturn true
+ on { sendCustomCommand(any(), any()) } doReturn customCommandFuture
}
private lateinit var underTest: Media3ActionFactory
@@ -105,7 +111,7 @@
kosmos.mediaLogger,
kosmos.looper,
handler,
- kosmos.testScope,
+ testScope,
kosmos.execution,
)
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 77be8c7..6ec38ba 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 @@
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 9f12b18..31a627f 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.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 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 @@
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 0fd7c98..fc1d73b 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 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 @@
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 0b56d7b..778c73f 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 @@
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/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelTest.kt
index 24f6b6d..7ab8ab9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelTest.kt
@@ -36,6 +36,7 @@
import com.android.systemui.scene.domain.startable.sceneContainerStartable
import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.shade.data.repository.shadeRepository
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.shade.shared.flag.DualShade
import com.android.systemui.testKosmos
@@ -124,6 +125,24 @@
assertThat(currentOverlays).doesNotContain(Overlays.QuickSettingsShade)
}
+ @Test
+ fun showHeader_showsOnNarrowScreen() =
+ testScope.runTest {
+ kosmos.shadeRepository.setShadeLayoutWide(false)
+ runCurrent()
+
+ assertThat(underTest.showHeader).isTrue()
+ }
+
+ @Test
+ fun showHeader_hidesOnWideScreen() =
+ testScope.runTest {
+ kosmos.shadeRepository.setShadeLayoutWide(true)
+ runCurrent()
+
+ assertThat(underTest.showHeader).isFalse()
+ }
+
private fun TestScope.lockDevice() {
val currentScene by collectLastValue(sceneInteractor.currentScene)
kosmos.powerInteractor.setAsleepForTest()
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 a6a1d4a..50fa9d2 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.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 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 @@
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 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 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 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 aceea90..ade5941 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 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 @@
@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 40f13bb..17076b4 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 @@
@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 @@
}
@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 5fbdfbf..0c992e0 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 @@
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 6f771175..9483f6d 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 @@
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 @@
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/featurepods/media/ui/viewmodel/MediaControlChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/featurepods/media/ui/viewmodel/MediaControlChipViewModelTest.kt
new file mode 100644
index 0000000..8650e4b
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/featurepods/media/ui/viewmodel/MediaControlChipViewModelTest.kt
@@ -0,0 +1,82 @@
+/*
+ * 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.featurepods.media.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.media.controls.data.repository.mediaFilterRepository
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.MediaDataLoadingModel
+import com.android.systemui.statusbar.featurepods.popups.shared.model.PopupChipModel
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class MediaControlChipViewModelTest : SysuiTestCase() {
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
+ private val underTest = kosmos.mediaControlChipViewModel
+
+ @Test
+ fun chip_noActiveMedia_IsHidden() =
+ kosmos.runTest {
+ val chip by collectLastValue(underTest.chip)
+
+ assertThat(chip).isInstanceOf(PopupChipModel.Hidden::class.java)
+ }
+
+ @Test
+ fun chip_activeMedia_IsShown() =
+ kosmos.runTest {
+ val chip by collectLastValue(underTest.chip)
+
+ val userMedia = MediaData(active = true, song = "test")
+ val instanceId = userMedia.instanceId
+
+ mediaFilterRepository.addSelectedUserMediaEntry(userMedia)
+ mediaFilterRepository.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(instanceId))
+
+ assertThat(chip).isInstanceOf(PopupChipModel.Shown::class.java)
+ }
+
+ @Test
+ fun chip_songNameChanges_chipTextUpdated() =
+ kosmos.runTest {
+ val chip by collectLastValue(underTest.chip)
+
+ val initialSongName = "Initial Song"
+ val newSongName = "New Song"
+ val userMedia = MediaData(active = true, song = initialSongName)
+ val instanceId = userMedia.instanceId
+
+ mediaFilterRepository.addSelectedUserMediaEntry(userMedia)
+ mediaFilterRepository.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(instanceId))
+
+ assertThat((chip as PopupChipModel.Shown).chipText).isEqualTo(initialSongName)
+
+ val updatedUserMedia = userMedia.copy(song = newSongName)
+ mediaFilterRepository.addSelectedUserMediaEntry(updatedUserMedia)
+
+ assertThat((chip as PopupChipModel.Shown).chipText).isEqualTo(newSongName)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModelTest.kt
index 14787e1..fcbf0fe 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModelTest.kt
@@ -22,7 +22,11 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
-import com.android.systemui.statusbar.chips.notification.shared.StatusBarPopupChips
+import com.android.systemui.media.controls.data.repository.mediaFilterRepository
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.MediaDataLoadingModel
+import com.android.systemui.statusbar.featurepods.popups.shared.model.PopupChipId
+import com.android.systemui.statusbar.featurepods.popups.StatusBarPopupChips
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
@@ -35,12 +39,28 @@
class StatusBarPopupChipsViewModelTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
+ private val mediaFilterRepository = kosmos.mediaFilterRepository
private val underTest = kosmos.statusBarPopupChipsViewModel
@Test
- fun popupChips_allHidden_empty() =
+ fun shownPopupChips_allHidden_empty() =
testScope.runTest {
- val latest by collectLastValue(underTest.popupChips)
- assertThat(latest).isEmpty()
+ val shownPopupChips by collectLastValue(underTest.shownPopupChips)
+ assertThat(shownPopupChips).isEmpty()
+ }
+
+ @Test
+ fun shownPopupChips_activeMedia_restHidden_mediaControlChipShown() =
+ testScope.runTest {
+ val shownPopupChips by collectLastValue(underTest.shownPopupChips)
+
+ val userMedia = MediaData(active = true, song = "test")
+ val instanceId = userMedia.instanceId
+
+ mediaFilterRepository.addSelectedUserMediaEntry(userMedia)
+ mediaFilterRepository.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(instanceId))
+
+ assertThat(shownPopupChips).hasSize(1)
+ assertThat(shownPopupChips!!.first().chipId).isEqualTo(PopupChipId.MediaControl)
}
}
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 26c6eb5..9227119 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.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 @@
@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/notification/stack/NotificationShelfTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
index 1a1af2e..87abd0a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
@@ -1,5 +1,6 @@
package com.android.systemui.statusbar.notification.stack
+import android.platform.test.annotations.EnableFlags
import android.service.notification.StatusBarNotification
import android.testing.TestableLooper.RunWithLooper
import android.view.LayoutInflater
@@ -20,6 +21,7 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
+import com.android.systemui.statusbar.notification.shared.NotificationMinimalism
import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.StackScrollAlgorithmState
import com.android.systemui.util.mockito.mock
import junit.framework.Assert.assertEquals
@@ -30,6 +32,7 @@
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.mock
+import org.mockito.Mockito.spy
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
@@ -59,7 +62,7 @@
.inflate(
/* resource = */ R.layout.status_bar_notification_shelf,
/* root = */ root,
- /* attachToRoot = */ false
+ /* attachToRoot = */ false,
) as NotificationShelf
whenever(ambientState.largeScreenShadeInterpolator).thenReturn(largeScreenShadeInterpolator)
@@ -128,6 +131,177 @@
}
@Test
+ @EnableFlags(NotificationMinimalism.FLAG_NAME)
+ fun testAlignment_splitShade_LTR() {
+ // Given: LTR mode, split shade
+ val shelfSpy =
+ prepareShelfSpy(shelf, rtl = false, splitShade = true, width = 100, actualWidth = 40)
+
+ // Then: shelf should align to end
+ assertTrue(shelfSpy.isAlignedToEnd)
+ assertTrue(shelfSpy.isAlignedToRight)
+ assertTrue(shelfSpy.mBackgroundNormal.alignToEnd)
+ assertTrue(shelfSpy.mShelfIcons.alignToEnd)
+ }
+
+ @Test
+ @EnableFlags(NotificationMinimalism.FLAG_NAME)
+ fun testAlignment_nonSplitShade_LTR() {
+ // Given: LTR mode, non split shade
+ val shelfSpy =
+ prepareShelfSpy(shelf, rtl = false, splitShade = false, width = 100, actualWidth = 40)
+
+ // Then: shelf should not align to end
+ assertFalse(shelfSpy.isAlignedToEnd)
+ assertFalse(shelfSpy.isAlignedToRight)
+ assertFalse(shelfSpy.mBackgroundNormal.alignToEnd)
+ assertFalse(shelfSpy.mShelfIcons.alignToEnd)
+ }
+
+ @Test
+ @EnableFlags(NotificationMinimalism.FLAG_NAME)
+ fun testAlignment_splitShade_RTL() {
+ // Given: RTL mode, split shade
+ val shelfSpy =
+ prepareShelfSpy(shelf, rtl = true, splitShade = true, width = 100, actualWidth = 40)
+
+ // Then: shelf should align to end, but to left due to RTL
+ assertTrue(shelfSpy.isAlignedToEnd)
+ assertFalse(shelfSpy.isAlignedToRight)
+ assertTrue(shelfSpy.mBackgroundNormal.alignToEnd)
+ assertTrue(shelfSpy.mShelfIcons.alignToEnd)
+ }
+
+ @Test
+ @EnableFlags(NotificationMinimalism.FLAG_NAME)
+ fun testAlignment_nonSplitShade_RTL() {
+ // Given: RTL mode, non split shade
+ val shelfSpy =
+ prepareShelfSpy(shelf, rtl = true, splitShade = false, width = 100, actualWidth = 40)
+
+ // Then: shelf should not align to end, but to right due to RTL
+ assertFalse(shelfSpy.isAlignedToEnd)
+ assertTrue(shelfSpy.isAlignedToRight)
+ assertFalse(shelfSpy.mBackgroundNormal.alignToEnd)
+ assertFalse(shelfSpy.mShelfIcons.alignToEnd)
+ }
+
+ @Test
+ @EnableFlags(NotificationMinimalism.FLAG_NAME)
+ fun testGetShelfLeftBound_splitShade_LTR() {
+ // Given: LTR mode, split shade
+ val shelfSpy =
+ prepareShelfSpy(shelf, rtl = false, splitShade = true, width = 100, actualWidth = 40)
+
+ // When: get the left bound of the shelf
+ val shelfLeftBound = shelfSpy.shelfLeftBound
+
+ // Then: should be equal to shelf's width - actual width
+ assertEquals(60f, shelfLeftBound)
+ }
+
+ @Test
+ @EnableFlags(NotificationMinimalism.FLAG_NAME)
+ fun testGetShelfRightBound_splitShade_LTR() {
+ // Given: LTR mode, split shade, width 100, actual width 40
+ val shelfSpy =
+ prepareShelfSpy(shelf, rtl = false, splitShade = true, width = 100, actualWidth = 40)
+
+ // Then: the right bound of the shelf should be equal to shelf's width
+ assertEquals(100f, shelfSpy.shelfRightBound)
+ }
+
+ @Test
+ @EnableFlags(NotificationMinimalism.FLAG_NAME)
+ fun testGetShelfLeftBound_nonSplitShade_LTR() {
+ // Given: LTR mode, non split shade
+ val shelfSpy =
+ prepareShelfSpy(shelf, rtl = false, splitShade = false, width = 100, actualWidth = 40)
+
+ // When: get the left bound of the shelf
+ val shelfLeftBound = shelfSpy.shelfLeftBound
+
+ // Then: should be equal to 0f
+ assertEquals(0f, shelfLeftBound)
+ }
+
+ @Test
+ @EnableFlags(NotificationMinimalism.FLAG_NAME)
+ fun testGetShelfRightBound_nonSplitShade_LTR() {
+ // Given: LTR mode, non split shade, width 100, actual width 40
+ val shelfSpy =
+ prepareShelfSpy(shelf, rtl = false, splitShade = false, width = 100, actualWidth = 40)
+
+ // Then: the right bound of the shelf should be equal to shelf's actual width
+ assertEquals(40f, shelfSpy.shelfRightBound)
+ }
+
+ @Test
+ @EnableFlags(NotificationMinimalism.FLAG_NAME)
+ fun testGetShelfLeftBound_splitShade_RTL() {
+ // Given: RTL mode, split shade
+ val shelfSpy =
+ prepareShelfSpy(shelf, rtl = true, splitShade = true, width = 100, actualWidth = 40)
+
+ // When: get the left bound of the shelf
+ val shelfLeftBound = shelfSpy.shelfLeftBound
+
+ // Then: should be equal to 0f
+ assertEquals(0f, shelfLeftBound)
+ }
+
+ @Test
+ @EnableFlags(NotificationMinimalism.FLAG_NAME)
+ fun testGetShelfRightBound_splitShade_RTL() {
+ // Given: RTL mode, split shade, width 100, actual width 40
+ val shelfSpy =
+ prepareShelfSpy(shelf, rtl = true, splitShade = true, width = 100, actualWidth = 40)
+
+ // Then: the right bound of the shelf should be equal to shelf's actual width
+ assertEquals(40f, shelfSpy.shelfRightBound)
+ }
+
+ @Test
+ @EnableFlags(NotificationMinimalism.FLAG_NAME)
+ fun testGetShelfLeftBound_nonSplitShade_RTL() {
+ // Given: RTL mode, non split shade
+ val shelfSpy =
+ prepareShelfSpy(shelf, rtl = true, splitShade = false, width = 100, actualWidth = 40)
+
+ // When: get the left bound of the shelf
+ val shelfLeftBound = shelfSpy.shelfLeftBound
+
+ // Then: should be equal to shelf's width - actual width
+ assertEquals(60f, shelfLeftBound)
+ }
+
+ @Test
+ @EnableFlags(NotificationMinimalism.FLAG_NAME)
+ fun testGetShelfRightBound_nonSplitShade_RTL() {
+ // Given: LTR mode, non split shade, width 100, actual width 40
+ val shelfSpy =
+ prepareShelfSpy(shelf, rtl = true, splitShade = false, width = 100, actualWidth = 40)
+
+ // Then: the right bound of the shelf should be equal to shelf's width
+ assertEquals(100f, shelfSpy.shelfRightBound)
+ }
+
+ private fun prepareShelfSpy(
+ shelf: NotificationShelf,
+ rtl: Boolean,
+ splitShade: Boolean,
+ width: Int,
+ actualWidth: Int,
+ ): NotificationShelf {
+ val shelfSpy = spy(shelf)
+ whenever(shelfSpy.isLayoutRtl).thenReturn(rtl)
+ whenever(ambientState.useSplitShade).thenReturn(splitShade)
+ whenever(shelfSpy.width).thenReturn(width)
+ shelfSpy.setActualWidth(actualWidth.toFloat())
+ return shelfSpy
+ }
+
+ @Test
fun getAmountInShelf_lastViewBelowShelf_completelyInShelf() {
val shelfClipStart = 0f
val viewStart = 1f
@@ -152,7 +326,7 @@
/* scrollingFast= */ false,
/* expandingAnimated= */ false,
/* isLastChild= */ true,
- shelfClipStart
+ shelfClipStart,
)
assertEquals(1f, amountInShelf)
}
@@ -182,7 +356,7 @@
/* scrollingFast= */ false,
/* expandingAnimated= */ false,
/* isLastChild= */ true,
- shelfClipStart
+ shelfClipStart,
)
assertEquals(1f, amountInShelf)
}
@@ -212,7 +386,7 @@
/* scrollingFast= */ false,
/* expandingAnimated= */ false,
/* isLastChild= */ true,
- shelfClipStart
+ shelfClipStart,
)
assertEquals(0.5f, amountInShelf)
}
@@ -241,7 +415,7 @@
/* scrollingFast= */ false,
/* expandingAnimated= */ false,
/* isLastChild= */ true,
- shelfClipStart
+ shelfClipStart,
)
assertEquals(0f, amountInShelf)
}
@@ -250,7 +424,7 @@
fun updateState_expansionChanging_shelfTransparent() {
updateState_expansionChanging_shelfAlphaUpdated(
expansionFraction = 0.25f,
- expectedAlpha = 0.0f
+ expectedAlpha = 0.0f,
)
}
@@ -260,7 +434,7 @@
updateState_expansionChanging_shelfAlphaUpdated(
expansionFraction = 0.85f,
- expectedAlpha = 0.0f
+ expectedAlpha = 0.0f,
)
}
@@ -281,7 +455,7 @@
updateState_expansionChanging_shelfAlphaUpdated(
expansionFraction = expansionFraction,
- expectedAlpha = 0.123f
+ expectedAlpha = 0.123f,
)
}
@@ -330,7 +504,7 @@
/* scrollingFast= */ false,
/* expandingAnimated= */ false,
/* isLastChild= */ true,
- shelfClipStart
+ shelfClipStart,
)
assertEquals(1f, amountInShelf)
}
@@ -628,7 +802,7 @@
private fun updateState_expansionChanging_shelfAlphaUpdated(
expansionFraction: Float,
- expectedAlpha: Float
+ expectedAlpha: Float,
) {
val sbnMock: StatusBarNotification = mock()
val mockEntry = mock<NotificationEntry>().apply { whenever(this.sbn).thenReturn(sbnMock) }
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 f76ee5e..cd3539d 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 @@
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 @@
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 647b5f8..cf512cd 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 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 @@
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 @@
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 @@
@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 @@
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 @@
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 @@
// 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 @@
/** 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 @@
/** 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 @@
@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 @@
@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 @@
.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 @@
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 @@
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/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt
index 0aaf89a..a9db0b7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt
@@ -23,6 +23,7 @@
import com.android.systemui.statusbar.chips.ui.model.MultipleOngoingActivityChipsModel
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.Idle
+import com.android.systemui.statusbar.featurepods.popups.shared.model.PopupChipModel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
@@ -41,6 +42,8 @@
override val ongoingActivityChips = MutableStateFlow(MultipleOngoingActivityChipsModel())
+ override val statusBarPopupChips = MutableStateFlow(emptyList<PopupChipModel.Shown>())
+
override val isHomeStatusBarAllowedByScene = MutableStateFlow(false)
override val shouldShowOperatorNameView = MutableStateFlow(false)
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 4a5ebd0..aa71b84 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 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.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 @@
BubbleDataRepository dataRepository,
IStatusBarService statusBarService,
WindowManager windowManager,
- WindowManagerShellWrapper windowManagerShellWrapper,
+ DisplayInsetsController displayInsetsController,
+ DisplayImeController displayImeController,
UserManager userManager,
LauncherApps launcherApps,
BubbleLogger bubbleLogger,
@@ -81,8 +83,8 @@
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
similarity index 84%
rename from packages/SystemUI/res/layout/ongoing_activity_chip.xml
rename to packages/SystemUI/res/layout/ongoing_activity_chip_content.xml
index 51217d4..6f42286 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 0000000..114fe6c
--- /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 0000000..81a7822
--- /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 1f4dea9..e4da472 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/layout/status_bar_notification_shelf.xml b/packages/SystemUI/res/layout/status_bar_notification_shelf.xml
index 58c5450..071b076 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_shelf.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_shelf.xml
@@ -24,11 +24,11 @@
android:clickable="true"
>
- <com.android.systemui.statusbar.notification.row.NotificationBackgroundView
+ <com.android.systemui.statusbar.notification.shelf.NotificationShelfBackgroundView
android:id="@+id/backgroundNormal"
android:layout_width="match_parent"
android:layout_height="match_parent" />
- <com.android.systemui.statusbar.phone.NotificationIconContainer
+ <com.android.systemui.statusbar.notification.shelf.NotificationShelfIconContainer
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 399c2d6..cd37c22 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 f6e49d9..94698bc 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/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
index ddc4d1c..16cf263 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
@@ -33,6 +33,7 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.ui.transitions.BlurConfig
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.Logger
import com.android.systemui.log.dagger.CommunalLog
@@ -92,6 +93,7 @@
@CommunalLog logBuffer: LogBuffer,
private val metricsLogger: CommunalMetricsLogger,
mediaCarouselController: MediaCarouselController,
+ blurConfig: BlurConfig,
) :
BaseCommunalViewModel(
communalSceneInteractor,
@@ -221,6 +223,15 @@
val isEnableWorkProfileDialogShowing: Flow<Boolean> =
_isEnableWorkProfileDialogShowing.asStateFlow()
+ val isUiBlurred: StateFlow<Boolean> =
+ if (Flags.bouncerUiRevamp()) {
+ keyguardInteractor.primaryBouncerShowing
+ } else {
+ MutableStateFlow(false)
+ }
+
+ val blurRadiusPx: Float = blurConfig.maxBlurRadiusPx / 2.0f
+
init {
// Initialize our media host for the UMO. This only needs to happen once and must be done
// before the MediaHierarchyManager attempts to move the UMO to the hub.
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 2997dd7..c039e01 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -100,10 +100,6 @@
// TODO(b/242908637): Tracking Bug
@JvmField val WALLPAPER_FULLSCREEN_PREVIEW = releasedFlag("wallpaper_fullscreen_preview")
- /** Whether the long-press gesture to open wallpaper picker is enabled. */
- // TODO(b/266242192): Tracking Bug
- @JvmField val LOCK_SCREEN_LONG_PRESS_ENABLED = releasedFlag("lock_screen_long_press_enabled")
-
/** Inflate and bind views upon emitting a blueprint value . */
// TODO(b/297365780): Tracking Bug
@JvmField val LAZY_INFLATE_KEYGUARD = releasedFlag("lazy_inflate_keyguard")
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractor.kt
index 274a1dd..a650300 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractor.kt
@@ -22,6 +22,7 @@
import android.content.IntentFilter
import android.view.accessibility.AccessibilityManager
import androidx.annotation.VisibleForTesting
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.internal.logging.UiEvent
import com.android.internal.logging.UiEventLogger
import com.android.systemui.broadcast.BroadcastDispatcher
@@ -29,7 +30,6 @@
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.res.R
@@ -51,7 +51,6 @@
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
-import com.android.app.tracing.coroutines.launchTraced as launch
/** Business logic for use-cases related to top-level touch handling in the lock screen. */
@OptIn(ExperimentalCoroutinesApi::class)
@@ -121,9 +120,7 @@
init {
if (isFeatureEnabled()) {
broadcastDispatcher
- .broadcastFlow(
- IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS),
- )
+ .broadcastFlow(IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS))
.onEach { hideMenu() }
.launchIn(scope)
}
@@ -188,8 +185,7 @@
}
private fun isFeatureEnabled(): Boolean {
- return featureFlags.isEnabled(Flags.LOCK_SCREEN_LONG_PRESS_ENABLED) &&
- context.resources.getBoolean(R.bool.long_press_keyguard_customize_lockscreen_enabled)
+ return context.resources.getBoolean(R.bool.long_press_keyguard_customize_lockscreen_enabled)
}
/** Updates application state to ask to show the menu. */
@@ -230,14 +226,11 @@
.toLong()
}
- enum class LogEvents(
- private val _id: Int,
- ) : UiEventLogger.UiEventEnum {
+ enum class LogEvents(private val _id: Int) : UiEventLogger.UiEventEnum {
@UiEvent(doc = "The lock screen was long-pressed and we showed the settings popup menu.")
LOCK_SCREEN_LONG_PRESS_POPUP_SHOWN(1292),
@UiEvent(doc = "The lock screen long-press popup menu was clicked.")
- LOCK_SCREEN_LONG_PRESS_POPUP_CLICKED(1293),
- ;
+ LOCK_SCREEN_LONG_PRESS_POPUP_CLICKED(1293);
override fun getId() = _id
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt
index 79360370..13c2ffb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt
@@ -19,6 +19,8 @@
import android.graphics.Rect
import android.view.View
+import android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED
+import android.widget.TextView
import androidx.core.view.isVisible
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
@@ -47,7 +49,7 @@
touchHandlingViewModel: KeyguardTouchHandlingViewModel,
rootViewModel: KeyguardRootViewModel?,
vibratorHelper: VibratorHelper,
- activityStarter: ActivityStarter
+ activityStarter: ActivityStarter,
): DisposableHandle {
val disposableHandle =
view.repeatWhenAttached {
@@ -57,19 +59,16 @@
view.animateVisibility(visible = isVisible)
if (isVisible) {
vibratorHelper.vibrate(KeyguardBottomAreaVibrations.Activated)
+ val textView = view.requireViewById(R.id.text) as TextView
view.setOnTouchListener(
- KeyguardSettingsButtonOnTouchListener(
- viewModel = viewModel,
- )
+ KeyguardSettingsButtonOnTouchListener(viewModel = viewModel)
)
IconViewBinder.bind(
icon = viewModel.icon,
view = view.requireViewById(R.id.icon),
)
- TextViewBinder.bind(
- view = view.requireViewById(R.id.text),
- viewModel = viewModel.text,
- )
+ TextViewBinder.bind(view = textView, viewModel = viewModel.text)
+ textView.sendAccessibilityEvent(TYPE_VIEW_FOCUSED)
}
}
}
@@ -108,15 +107,12 @@
}
/** Opens the wallpaper picker screen after the device is unlocked by the user. */
- private fun navigateToLockScreenSettings(
- activityStarter: ActivityStarter,
- view: View,
- ) {
+ private fun navigateToLockScreenSettings(activityStarter: ActivityStarter, view: View) {
activityStarter.postStartActivityDismissingKeyguard(
WallpaperPickerIntentUtils.getIntent(view.context, LAUNCH_SOURCE_KEYGUARD),
/* delay= */ 0,
/* animationController= */ ActivityTransitionAnimator.Controller.fromView(view),
- /* customMessage= */ view.context.getString(R.string.keyguard_unlock_to_customize_ls)
+ /* customMessage= */ view.context.getString(R.string.keyguard_unlock_to_customize_ls),
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/Media3ActionFactory.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/Media3ActionFactory.kt
index 913aa6f..0954482 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/Media3ActionFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/Media3ActionFactory.kt
@@ -43,6 +43,7 @@
import com.android.systemui.media.controls.util.SessionTokenFactory
import com.android.systemui.res.R
import com.android.systemui.util.concurrency.Execution
+import java.util.concurrent.ExecutionException
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
@@ -71,7 +72,7 @@
*
* @param packageName Package name for the media app
* @param controller The framework [MediaController] for the session
- * @return The media action buttons, or null if the session token is null
+ * @return The media action buttons, or null if cannot be created for this session
*/
suspend fun createActionsFromSession(
packageName: String,
@@ -80,6 +81,10 @@
// Get the Media3 controller using the legacy token
val token = tokenFactory.createTokenFromLegacy(sessionToken)
val m3controller = controllerFactory.create(token, looper)
+ if (m3controller == null) {
+ logger.logCreateFailed(packageName, "createActionsFromSession")
+ return null
+ }
// Build button info
val buttons = suspendCancellableCoroutine { continuation ->
@@ -89,13 +94,14 @@
val result = getMedia3Actions(packageName, m3controller, token)
continuation.resumeWith(Result.success(result))
} finally {
- m3controller.release()
+ m3controller.tryRelease(packageName, logger)
}
}
handler.post(runnable)
continuation.invokeOnCancellation {
// Ensure controller is released, even if loading was cancelled partway through
- handler.post(m3controller::release)
+ val releaseRunnable = Runnable { m3controller.tryRelease(packageName, logger) }
+ handler.post(releaseRunnable)
handler.removeCallbacks(runnable)
}
}
@@ -127,11 +133,12 @@
com.android.internal.R.drawable.progress_small_material,
)
} else {
- getStandardAction(m3controller, token, Player.COMMAND_PLAY_PAUSE)
+ getStandardAction(packageName, m3controller, token, Player.COMMAND_PLAY_PAUSE)
}
val prevButton =
getStandardAction(
+ packageName,
m3controller,
token,
Player.COMMAND_SEEK_TO_PREVIOUS,
@@ -139,6 +146,7 @@
)
val nextButton =
getStandardAction(
+ packageName,
m3controller,
token,
Player.COMMAND_SEEK_TO_NEXT,
@@ -208,6 +216,7 @@
* @return A [MediaAction] representing the first supported command, or null if not supported
*/
private fun getStandardAction(
+ packageName: String,
controller: Media3Controller,
token: SessionToken,
vararg commands: @Player.Command Int,
@@ -222,14 +231,14 @@
if (!controller.isPlaying) {
MediaAction(
context.getDrawable(R.drawable.ic_media_play),
- { executeAction(token, Player.COMMAND_PLAY_PAUSE) },
+ { executeAction(packageName, token, Player.COMMAND_PLAY_PAUSE) },
context.getString(R.string.controls_media_button_play),
context.getDrawable(R.drawable.ic_media_play_container),
)
} else {
MediaAction(
context.getDrawable(R.drawable.ic_media_pause),
- { executeAction(token, Player.COMMAND_PLAY_PAUSE) },
+ { executeAction(packageName, token, Player.COMMAND_PLAY_PAUSE) },
context.getString(R.string.controls_media_button_pause),
context.getDrawable(R.drawable.ic_media_pause_container),
)
@@ -238,7 +247,7 @@
else -> {
MediaAction(
icon = getIconForAction(command),
- action = { executeAction(token, command) },
+ action = { executeAction(packageName, token, command) },
contentDescription = getDescriptionForAction(command),
background = null,
)
@@ -256,7 +265,7 @@
): MediaAction {
return MediaAction(
getIconForAction(customAction, packageName),
- { executeAction(token, Player.COMMAND_INVALID, customAction) },
+ { executeAction(packageName, token, Player.COMMAND_INVALID, customAction) },
customAction.displayName,
null,
)
@@ -308,12 +317,17 @@
}
private fun executeAction(
+ packageName: String,
token: SessionToken,
command: Int,
customAction: CommandButton? = null,
) {
bgScope.launch {
val controller = controllerFactory.create(token, looper)
+ if (controller == null) {
+ logger.logCreateFailed(packageName, "executeAction")
+ return@launch
+ }
handler.post {
try {
when (command) {
@@ -347,9 +361,17 @@
else -> logger.logMedia3UnsupportedCommand(command.toString())
}
} finally {
- controller.release()
+ controller.tryRelease(packageName, logger)
}
}
}
}
}
+
+private fun Media3Controller.tryRelease(packageName: String, logger: MediaLogger) {
+ try {
+ this.release()
+ } catch (e: ExecutionException) {
+ logger.logReleaseFailed(packageName, e.cause.toString())
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/shared/MediaLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/shared/MediaLogger.kt
index 0b598c1..c52268e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/shared/MediaLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/shared/MediaLogger.kt
@@ -144,6 +144,30 @@
buffer.log(TAG, LogLevel.DEBUG, { str1 = command }, { "Unsupported media3 command $str1" })
}
+ fun logCreateFailed(pkg: String, method: String) {
+ buffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = pkg
+ str2 = method
+ },
+ { "Controller create failed for $str1 ($str2)" },
+ )
+ }
+
+ fun logReleaseFailed(pkg: String, cause: String) {
+ buffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = pkg
+ str2 = cause
+ },
+ { "Controller release failed for $str1 ($str2)" },
+ )
+ }
+
companion object {
private const val TAG = "MediaLog"
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaControllerFactory.kt b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaControllerFactory.kt
index d815852..7b9e18a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaControllerFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaControllerFactory.kt
@@ -19,13 +19,17 @@
import android.media.session.MediaController
import android.media.session.MediaSession
import android.os.Looper
+import android.util.Log
import androidx.concurrent.futures.await
import androidx.media3.session.MediaController as Media3Controller
import androidx.media3.session.SessionToken
+import java.util.concurrent.ExecutionException
import javax.inject.Inject
/** Testable wrapper for media controller construction */
open class MediaControllerFactory @Inject constructor(private val context: Context) {
+ private val TAG = "MediaControllerFactory"
+
/**
* Creates a new [MediaController] from the framework session token.
*
@@ -41,10 +45,18 @@
* @param token The token for the session
* @param looper The looper that will be used for this controller's operations
*/
- open suspend fun create(token: SessionToken, looper: Looper): Media3Controller {
- return Media3Controller.Builder(context, token)
- .setApplicationLooper(looper)
- .buildAsync()
- .await()
+ open suspend fun create(token: SessionToken, looper: Looper): Media3Controller? {
+ try {
+ return Media3Controller.Builder(context, token)
+ .setApplicationLooper(looper)
+ .buildAsync()
+ .await()
+ } catch (e: ExecutionException) {
+ if (e.cause is SecurityException) {
+ // The session rejected the connection
+ Log.d(TAG, "SecurityException creating media3 controller")
+ }
+ return null
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
index 9e50fb8..64256f9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
@@ -268,8 +268,11 @@
// Init bottom buttons
mDoneButton.setOnClickListener(v -> dismiss());
mStopButton.setOnClickListener(v -> onStopButtonClick());
- mMediaMetadataSectionLayout.setOnClickListener(
- mMediaSwitchingController::tryToLaunchMediaApplication);
+ if (mMediaSwitchingController.getAppLaunchIntent() != null) {
+ // For a11y purposes only add listener if a section is clickable.
+ mMediaMetadataSectionLayout.setOnClickListener(
+ mMediaSwitchingController::tryToLaunchMediaApplication);
+ }
mDismissing = false;
}
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 debb667..a19c9b3 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 @@
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 @@
.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 ad027b4..30c2adf 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.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 @@
});
}
} 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 fc82592..ec8d30b 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 @@
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 @@
}
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 85aa674..9453447 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 @@
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/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModel.kt
index 8ef51af..c0c0aea 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModel.kt
@@ -16,8 +16,10 @@
package com.android.systemui.qs.ui.viewmodel
+import androidx.compose.runtime.getValue
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.lifecycle.Hydrator
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.domain.interactor.ShadeInteractor
@@ -28,6 +30,8 @@
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.launch
/**
* Models UI state used to render the content of the quick settings shade overlay.
@@ -44,10 +48,21 @@
quickSettingsContainerViewModelFactory: QuickSettingsContainerViewModel.Factory,
) : ExclusiveActivatable() {
+ private val hydrator = Hydrator("QuickSettingsContainerViewModel.hydrator")
+
+ val showHeader: Boolean by
+ hydrator.hydratedStateOf(
+ traceName = "showHeader",
+ initialValue = !shadeInteractor.isShadeLayoutWide.value,
+ source = shadeInteractor.isShadeLayoutWide.map { !it },
+ )
+
val quickSettingsContainerViewModel = quickSettingsContainerViewModelFactory.create(false)
override suspend fun onActivated(): Nothing {
coroutineScope {
+ launch { hydrator.activate() }
+
launch {
sceneInteractor.currentScene.collect { currentScene ->
when (currentScene) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
index d7463f8..9ee99e4 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.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 @@
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 @@
new UserTracker.Callback() {
@Override
public void onUserChanged(int newUser, @NonNull Context userContext) {
- stopRecording();
+ stopRecording(StopReason.STOP_USER_SWITCH);
}
};
@@ -240,9 +242,11 @@
}
/**
- * 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 @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 8c207d1..f7b5271 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.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 @@
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 @@
// 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 @@
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 @@
setTapsVisible(mOriginalShowTaps);
try {
if (getRecorder() != null) {
- getRecorder().end();
+ getRecorder().end(stopReason);
}
saveRecording(userId);
} catch (RuntimeException exception) {
@@ -598,7 +601,8 @@
* @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 @@
@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 2ca0621..f4455bf 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.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 @@
/**
* 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 @@
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 @@
@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 @@
* 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 9eeb3b9..b6b8ffa 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 @@
val screenRecordState: Flow<ScreenRecordModel>
/** Stops the recording. */
- suspend fun stopRecording()
+ suspend fun stopRecording(@StopReason stopReason: Int)
}
@SysUISingleton
@@ -95,7 +96,7 @@
}
}
- 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/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index d523bc1..48cf7a83 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -48,6 +48,9 @@
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
+import com.android.systemui.statusbar.notification.shared.NotificationMinimalism;
+import com.android.systemui.statusbar.notification.shelf.NotificationShelfBackgroundView;
+import com.android.systemui.statusbar.notification.shelf.NotificationShelfIconContainer;
import com.android.systemui.statusbar.notification.stack.AmbientState;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
@@ -76,7 +79,11 @@
private static final SourceType BASE_VALUE = SourceType.from("BaseValue");
private static final SourceType SHELF_SCROLL = SourceType.from("ShelfScroll");
- private NotificationIconContainer mShelfIcons;
+ @VisibleForTesting
+ public NotificationShelfIconContainer mShelfIcons;
+ // This field hides mBackgroundNormal from super class for short-shelf alignment
+ @VisibleForTesting
+ public NotificationShelfBackgroundView mBackgroundNormal;
private boolean mHideBackground;
private int mStatusBarHeight;
private boolean mEnableNotificationClipping;
@@ -116,6 +123,8 @@
mShelfIcons.setClipChildren(false);
mShelfIcons.setClipToPadding(false);
+ mBackgroundNormal = (NotificationShelfBackgroundView) super.mBackgroundNormal;
+
setClipToActualHeight(false);
setClipChildren(false);
setClipToPadding(false);
@@ -268,19 +277,37 @@
}
}
- private void setActualWidth(float actualWidth) {
+ /**
+ * Set the actual width of the shelf, this will only differ from width for short shelves.
+ */
+ @VisibleForTesting
+ public void setActualWidth(float actualWidth) {
setBackgroundWidth((int) actualWidth);
if (mShelfIcons != null) {
+ mShelfIcons.setAlignToEnd(isAlignedToEnd());
mShelfIcons.setActualLayoutWidth((int) actualWidth);
}
mActualWidth = actualWidth;
}
@Override
+ public void setBackgroundWidth(int width) {
+ super.setBackgroundWidth(width);
+ if (!NotificationMinimalism.isEnabled()) {
+ return;
+ }
+ if (mBackgroundNormal != null) {
+ mBackgroundNormal.setAlignToEnd(isAlignedToEnd());
+ }
+ }
+
+ @Override
public void getBoundsOnScreen(Rect outRect, boolean clipToParent) {
super.getBoundsOnScreen(outRect, clipToParent);
final int actualWidth = getActualWidth();
- if (isLayoutRtl()) {
+ final boolean alignedToRight = NotificationMinimalism.isEnabled() ? isAlignedToRight() :
+ isLayoutRtl();
+ if (alignedToRight) {
outRect.left = outRect.right - actualWidth;
} else {
outRect.right = outRect.left + actualWidth;
@@ -326,11 +353,17 @@
*/
@Override
public boolean pointInView(float localX, float localY, float slop) {
- final float containerWidth = getWidth();
- final float shelfWidth = getActualWidth();
+ final float left, right;
- final float left = isLayoutRtl() ? containerWidth - shelfWidth : 0;
- final float right = isLayoutRtl() ? containerWidth : shelfWidth;
+ if (NotificationMinimalism.isEnabled()) {
+ left = getShelfLeftBound();
+ right = getShelfRightBound();
+ } else {
+ final float containerWidth = getWidth();
+ final float shelfWidth = getActualWidth();
+ left = isLayoutRtl() ? containerWidth - shelfWidth : 0;
+ right = isLayoutRtl() ? containerWidth : shelfWidth;
+ }
final float top = mClipTopAmount;
final float bottom = getActualHeight();
@@ -339,10 +372,53 @@
&& isYInView(localY, slop, top, bottom);
}
+ /**
+ * @return The left boundary of the shelf.
+ */
+ @VisibleForTesting
+ public float getShelfLeftBound() {
+ if (isAlignedToRight()) {
+ return getWidth() - getActualWidth();
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * @return The right boundary of the shelf.
+ */
+ @VisibleForTesting
+ public float getShelfRightBound() {
+ if (isAlignedToRight()) {
+ return getWidth();
+ } else {
+ return getActualWidth();
+ }
+ }
+
+ @VisibleForTesting
+ public boolean isAlignedToRight() {
+ return isAlignedToEnd() ^ isLayoutRtl();
+ }
+
+ /**
+ * When notification minimalism is on, on split shade, we want the notification shelf to align
+ * to the layout end (right for LTR; left for RTL).
+ * @return whether to align with the minimalism split shade style
+ */
+ @VisibleForTesting
+ public boolean isAlignedToEnd() {
+ if (!NotificationMinimalism.isEnabled()) {
+ return false;
+ }
+ return mAmbientState.getUseSplitShade();
+ }
+
@Override
public void updateBackgroundColors() {
super.updateBackgroundColors();
ColorUpdateLogger colorUpdateLogger = ColorUpdateLogger.getInstance();
+
if (colorUpdateLogger != null) {
colorUpdateLogger.logEvent("Shelf.updateBackgroundColors()",
"normalBgColor=" + hexColorString(getNormalBgColor())
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 85b50d3..de08e38 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 @@
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 b3dbf29..229cef9 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 @@
/** 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 a7dbb47..bcd8cfa 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 @@
)
}
- 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 f5952f4..0b5e669 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 @@
/** 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 0c4c1a7..c40df98 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 @@
// 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 @@
}
// 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 0000000..f790dc9
--- /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/featurepods/media/domain/interactor/MediaControlChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/media/domain/interactor/MediaControlChipInteractor.kt
index 85c67f5..4e68bee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/media/domain/interactor/MediaControlChipInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/media/domain/interactor/MediaControlChipInteractor.kt
@@ -41,14 +41,14 @@
class MediaControlChipInteractor
@Inject
constructor(
- @Background private val applicationScope: CoroutineScope,
+ @Background private val backgroundScope: CoroutineScope,
mediaFilterRepository: MediaFilterRepository,
) {
private val currentMediaControls: StateFlow<List<MediaCommonModel.MediaControl>> =
mediaFilterRepository.currentMedia
.map { mediaList -> mediaList.filterIsInstance<MediaCommonModel.MediaControl>() }
.stateIn(
- scope = applicationScope,
+ scope = backgroundScope,
started = SharingStarted.WhileSubscribed(),
initialValue = emptyList(),
)
@@ -64,7 +64,7 @@
?.toMediaControlChipModel()
}
.stateIn(
- scope = applicationScope,
+ scope = backgroundScope,
started = SharingStarted.WhileSubscribed(),
initialValue = null,
)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/media/ui/viewmodel/MediaControlChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/media/ui/viewmodel/MediaControlChipViewModel.kt
new file mode 100644
index 0000000..3e854b4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/media/ui/viewmodel/MediaControlChipViewModel.kt
@@ -0,0 +1,87 @@
+/*
+ * 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.featurepods.media.ui.viewmodel
+
+import android.content.Context
+import com.android.systemui.common.shared.model.ContentDescription
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.statusbar.featurepods.media.domain.interactor.MediaControlChipInteractor
+import com.android.systemui.statusbar.featurepods.media.shared.model.MediaControlChipModel
+import com.android.systemui.statusbar.featurepods.popups.shared.model.PopupChipId
+import com.android.systemui.statusbar.featurepods.popups.shared.model.PopupChipModel
+import com.android.systemui.statusbar.featurepods.popups.ui.viewmodel.StatusBarPopupChipViewModel
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+
+/**
+ * [StatusBarPopupChipViewModel] for a media control chip in the status bar. This view model is
+ * responsible for converting the [MediaControlChipModel] to a [PopupChipModel] that can be used to
+ * display a media control chip.
+ */
+@SysUISingleton
+class MediaControlChipViewModel
+@Inject
+constructor(
+ @Background private val backgroundScope: CoroutineScope,
+ @Application private val applicationContext: Context,
+ mediaControlChipInteractor: MediaControlChipInteractor,
+) : StatusBarPopupChipViewModel {
+
+ /**
+ * A [StateFlow] of the current [PopupChipModel]. This flow emits a new [PopupChipModel]
+ * whenever the underlying [MediaControlChipModel] changes.
+ */
+ override val chip: StateFlow<PopupChipModel> =
+ mediaControlChipInteractor.mediaControlModel
+ .map { mediaControlModel -> toPopupChipModel(mediaControlModel, applicationContext) }
+ .stateIn(
+ backgroundScope,
+ SharingStarted.WhileSubscribed(),
+ PopupChipModel.Hidden(PopupChipId.MediaControl),
+ )
+}
+
+private fun toPopupChipModel(model: MediaControlChipModel?, context: Context): PopupChipModel {
+ if (model == null || model.songName.isNullOrEmpty()) {
+ return PopupChipModel.Hidden(PopupChipId.MediaControl)
+ }
+
+ val contentDescription = model.appName?.let { ContentDescription.Loaded(description = it) }
+ return PopupChipModel.Shown(
+ chipId = PopupChipId.MediaControl,
+ icon =
+ model.appIcon?.loadDrawable(context)?.let {
+ Icon.Loaded(drawable = it, contentDescription = contentDescription)
+ }
+ ?: Icon.Resource(
+ res = com.android.internal.R.drawable.ic_audio_media,
+ contentDescription = contentDescription,
+ ),
+ chipText = model.songName.toString(),
+ // TODO(b/385202114): Show a popup containing the media carousal when the chip is toggled.
+ onToggle = {},
+ // TODO(b/385202193): Add support for clicking on the icon on a media chip.
+ onIconPressed = {},
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/StatusBarPopupChips.kt b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/StatusBarPopupChips.kt
index 9f523fc..a9f72ff2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/StatusBarPopupChips.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/StatusBarPopupChips.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.chips.notification.shared
+package com.android.systemui.statusbar.featurepods.popups
import com.android.systemui.Flags
import com.android.systemui.flags.FlagToken
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/shared/model/PopupChipModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/shared/model/PopupChipModel.kt
index 1663aeb..0a6c4d0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/shared/model/PopupChipModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/shared/model/PopupChipModel.kt
@@ -23,7 +23,7 @@
* displaying its popup at a time.
*/
sealed class PopupChipId(val value: String) {
- data object MediaControls : PopupChipId("MediaControls")
+ data object MediaControl : PopupChipId("MediaControl")
}
/** Model for individual status bar popup chips. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/compose/StatusBarPopupChipsContainer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/compose/StatusBarPopupChipsContainer.kt
new file mode 100644
index 0000000..56bbd74
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/compose/StatusBarPopupChipsContainer.kt
@@ -0,0 +1,37 @@
+/*
+ * 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.featurepods.popups.ui.compose
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Row
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import com.android.systemui.statusbar.featurepods.popups.shared.model.PopupChipModel
+
+/** Container view that holds all right hand side chips in the status bar. */
+@Composable
+fun StatusBarPopupChipsContainer(chips: List<PopupChipModel.Shown>, modifier: Modifier = Modifier) {
+ // TODO(b/385353140): Add padding and spacing for this container according to UX specs.
+ Box {
+ Row(verticalAlignment = Alignment.CenterVertically) {
+ // TODO(b/385352859): Show `StatusBarPopupChip` here instead of `Text` once it is ready.
+ chips.forEach { chip -> Text(text = chip.chipText) }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModel.kt
index b390f29..caa8e6c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModel.kt
@@ -18,13 +18,16 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.statusbar.featurepods.media.ui.viewmodel.MediaControlChipViewModel
+import com.android.systemui.statusbar.featurepods.popups.StatusBarPopupChips
import com.android.systemui.statusbar.featurepods.popups.shared.model.PopupChipId
import com.android.systemui.statusbar.featurepods.popups.shared.model.PopupChipModel
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
@@ -33,16 +36,29 @@
* PopupChipModels.
*/
@SysUISingleton
-class StatusBarPopupChipsViewModel @Inject constructor(@Background scope: CoroutineScope) {
+class StatusBarPopupChipsViewModel
+@Inject
+constructor(
+ @Background scope: CoroutineScope,
+ mediaControlChipViewModel: MediaControlChipViewModel,
+) {
private data class PopupChipBundle(
- val media: PopupChipModel = PopupChipModel.Hidden(chipId = PopupChipId.MediaControls)
+ val media: PopupChipModel = PopupChipModel.Hidden(chipId = PopupChipId.MediaControl)
)
- private val incomingPopupChipBundle: Flow<PopupChipBundle?> =
- flowOf(null).stateIn(scope, SharingStarted.Lazily, PopupChipBundle())
+ private val incomingPopupChipBundle: StateFlow<PopupChipBundle?> =
+ mediaControlChipViewModel.chip
+ .map { chip -> PopupChipBundle(media = chip) }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), PopupChipBundle())
- val popupChips: Flow<List<PopupChipModel>> =
- incomingPopupChipBundle
- .map { _ -> listOf(null).filterIsInstance<PopupChipModel.Shown>() }
- .stateIn(scope, SharingStarted.Lazily, emptyList())
+ val shownPopupChips: StateFlow<List<PopupChipModel.Shown>> =
+ if (StatusBarPopupChips.isEnabled) {
+ incomingPopupChipBundle
+ .map { bundle ->
+ listOfNotNull(bundle?.media).filterIsInstance<PopupChipModel.Shown>()
+ }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), emptyList())
+ } else {
+ MutableStateFlow(emptyList<PopupChipModel.Shown>()).asStateFlow()
+ }
}
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 bb16484..3957462 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 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 7d28276..df2eb08 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_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 @@
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 @@
// 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 74809fd..258d80c 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 @@
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 @@
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 @@
fun build() =
PromotedNotificationContentModel(
identity = Identity(key, style),
+ wasPromotedAutomatically = wasPromotedAutomatically,
skeletonSmallIcon = skeletonSmallIcon,
appName = appName,
subText = subText,
@@ -134,5 +142,18 @@
@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/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
index e440d27..dd3a9c9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
@@ -169,12 +169,12 @@
&& !mExpandAnimationRunning) {
bottom -= mClipBottomAmount;
}
- final boolean isRtl = isLayoutRtl();
+ final boolean alignedToRight = isAlignedToRight();
final int width = getWidth();
final int actualWidth = getActualWidth();
- int left = isRtl ? width - actualWidth : 0;
- int right = isRtl ? width : actualWidth;
+ int left = alignedToRight ? width - actualWidth : 0;
+ int right = alignedToRight ? width : actualWidth;
if (mExpandAnimationRunning) {
// Horizontally center this background view inside of the container
@@ -185,6 +185,15 @@
return new Rect(left, top, right, bottom);
}
+ /**
+ * @return Whether the background view should be right-aligned. This only matters if the
+ * actualWidth is different than the full (measured) width. In other words, this is used to
+ * define the short-shelf alignment.
+ */
+ protected boolean isAlignedToRight() {
+ return isLayoutRtl();
+ }
+
private void draw(Canvas canvas, Drawable drawable) {
NotificationAddXOnHoverToDismiss.assertInLegacyMode();
@@ -196,12 +205,13 @@
&& !mExpandAnimationRunning) {
bottom -= mClipBottomAmount;
}
- final boolean isRtl = isLayoutRtl();
+
+ final boolean alignedToRight = isAlignedToRight();
final int width = getWidth();
final int actualWidth = getActualWidth();
- int left = isRtl ? width - actualWidth : 0;
- int right = isRtl ? width : actualWidth;
+ int left = alignedToRight ? width - actualWidth : 0;
+ int right = alignedToRight ? width : actualWidth;
if (mExpandAnimationRunning) {
// Horizontally center this background view inside of the container
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/NotificationShelfBackgroundView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/NotificationShelfBackgroundView.kt
new file mode 100644
index 0000000..d7eea01
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/NotificationShelfBackgroundView.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.systemui.statusbar.notification.shelf
+
+import android.content.Context
+import android.util.AttributeSet
+import androidx.annotation.VisibleForTesting
+import com.android.systemui.statusbar.notification.row.NotificationBackgroundView
+import com.android.systemui.statusbar.notification.shared.NotificationMinimalism
+
+/** The background view for the NotificationShelf. */
+class NotificationShelfBackgroundView
+@JvmOverloads
+constructor(context: Context, attrs: AttributeSet? = null) :
+ NotificationBackgroundView(context, attrs) {
+
+ /** Whether the notification shelf is aligned to end, need to keep persistent with the shelf. */
+ var alignToEnd = false
+
+ /** @return whether the alignment of the notification shelf is right. */
+ @VisibleForTesting
+ public override fun isAlignedToRight(): Boolean {
+ if (!NotificationMinimalism.isEnabled) {
+ return super.isAlignedToRight()
+ }
+ return alignToEnd xor isLayoutRtl
+ }
+
+ override fun toDumpString(): String {
+ return super.toDumpString() + " alignToEnd=" + alignToEnd
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/NotificationShelfIconContainer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/NotificationShelfIconContainer.kt
new file mode 100644
index 0000000..64d1654
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/NotificationShelfIconContainer.kt
@@ -0,0 +1,90 @@
+/*
+ * 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.notification.shelf
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.View
+import com.android.systemui.statusbar.notification.shared.NotificationMinimalism
+import com.android.systemui.statusbar.phone.NotificationIconContainer
+import kotlin.math.max
+
+/** The NotificationIconContainer for the NotificationShelf. */
+class NotificationShelfIconContainer
+@JvmOverloads
+constructor(context: Context, attrs: AttributeSet? = null) :
+ NotificationIconContainer(context, attrs) {
+
+ /** Whether the notification shelf is aligned to end. */
+ var alignToEnd = false
+
+ /**
+ * @return The left boundary (not the RTL compatible start) of the area that icons can be added.
+ */
+ override fun getLeftBound(): Float {
+ if (!NotificationMinimalism.isEnabled) {
+ return super.getLeftBound()
+ }
+
+ if (isAlignedToRight) {
+ return (max(width - actualWidth, 0) + actualPaddingStart)
+ }
+ return actualPaddingStart
+ }
+
+ /**
+ * @return The right boundary (not the RTL compatible end) of the area that icons can be added.
+ */
+ override fun getRightBound(): Float {
+ if (!NotificationMinimalism.isEnabled) {
+ return super.getRightBound()
+ }
+
+ if (isAlignedToRight) {
+ return width - actualPaddingEnd
+ }
+ return actualWidth - actualPaddingEnd
+ }
+
+ /**
+ * For RTL, the icons' x positions should be mirrored around the middle of the shelf so that the
+ * icons are also added to the shelf from right to left. This function should only be called
+ * when RTL.
+ */
+ override fun getRtlIconTranslationX(iconState: IconState, iconView: View): Float {
+ if (!NotificationMinimalism.isEnabled) {
+ return super.getRtlIconTranslationX(iconState, iconView)
+ }
+
+ if (!isLayoutRtl) {
+ return iconState.xTranslation
+ }
+
+ if (isAlignedToRight) {
+ return width * 2 - actualWidth - iconState.xTranslation - iconView.width
+ }
+ return actualWidth - iconState.xTranslation - iconView.width
+ }
+
+ private val isAlignedToRight: Boolean
+ get() {
+ if (!NotificationMinimalism.isEnabled) {
+ return isLayoutRtl
+ }
+ return alignToEnd xor isLayoutRtl
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 38a7035..50e5a23 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -866,9 +866,9 @@
y = (int) mAmbientState.getHeadsUpTop();
drawDebugInfo(canvas, y, Color.GREEN, /* label= */ "getHeadsUpTop() = " + y);
- y += getTopHeadsUpHeight();
+ y = (int) (mAmbientState.getStackTop() + mScrollViewFields.getIntrinsicStackHeight());
drawDebugInfo(canvas, y, Color.BLUE,
- /* label= */ "getHeadsUpTop() + getTopHeadsUpHeight() = " + y);
+ /* label= */ "getStackTop() + getIntrinsicStackHeight() = " + y);
return; // the rest of the fields are not important in Flexiglass
}
@@ -2612,20 +2612,13 @@
if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
final int shelfIntrinsicHeight = mShelf != null ? mShelf.getIntrinsicHeight() : 0;
- final int footerIntrinsicHeight =
- mFooterView != null ? mFooterView.getIntrinsicHeight() : 0;
final int notificationsHeight = (int) mNotificationStackSizeCalculator.computeHeight(
/* notificationStackScrollLayout= */ this,
mMaxDisplayedNotifications,
shelfIntrinsicHeight
);
- // When there is a limit in the max number of notifications, we never display the footer.
- final int fullStackHeight = mMaxDisplayedNotifications != -1
- ? notificationsHeight
- : notificationsHeight + footerIntrinsicHeight + mBottomPadding;
-
- if (mScrollViewFields.getIntrinsicStackHeight() != fullStackHeight) {
- mScrollViewFields.setIntrinsicStackHeight(fullStackHeight);
+ if (mScrollViewFields.getIntrinsicStackHeight() != notificationsHeight) {
+ mScrollViewFields.setIntrinsicStackHeight(notificationsHeight);
notifyStackHeightChangedListeners();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index ecd62bd..c396512 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -198,7 +198,7 @@
Paint paint = new Paint();
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.STROKE);
- canvas.drawRect(getActualPaddingStart(), 0, getLayoutEnd(), getHeight(), paint);
+ canvas.drawRect(getActualPaddingStart(), 0, getRightBound(), getHeight(), paint);
if (DEBUG_OVERFLOW) {
if (mLastVisibleIconState == null) {
@@ -469,11 +469,11 @@
* If this is not a whole number, the fraction means by how much the icon is appearing.
*/
public void calculateIconXTranslations() {
- float translationX = getActualPaddingStart();
+ float translationX = getLeftBound();
int firstOverflowIndex = -1;
int childCount = getChildCount();
int maxVisibleIcons = mMaxIcons;
- float layoutEnd = getLayoutEnd();
+ float layoutRight = getRightBound();
mVisualOverflowStart = 0;
mFirstVisibleIconState = null;
for (int i = 0; i < childCount; i++) {
@@ -495,7 +495,7 @@
final boolean forceOverflow = shouldForceOverflow(i, mSpeedBumpIndex,
iconState.iconAppearAmount, maxVisibleIcons);
final boolean isOverflowing = forceOverflow || isOverflowing(
- /* isLastChild= */ i == childCount - 1, translationX, layoutEnd, mIconSize);
+ /* isLastChild= */ i == childCount - 1, translationX, layoutRight, mIconSize);
// First icon to overflow.
if (firstOverflowIndex == -1 && isOverflowing) {
@@ -536,8 +536,7 @@
for (int i = 0; i < childCount; i++) {
View view = getChildAt(i);
IconState iconState = mIconStates.get(view);
- iconState.setXTranslation(
- getWidth() - iconState.getXTranslation() - view.getWidth());
+ iconState.setXTranslation(getRtlIconTranslationX(iconState, view));
}
}
if (mIsolatedIcon != null) {
@@ -553,6 +552,11 @@
}
}
+ /** We need this to keep icons ordered from right to left when RTL. */
+ protected float getRtlIconTranslationX(IconState iconState, View iconView) {
+ return getWidth() - iconState.getXTranslation() - iconView.getWidth();
+ }
+
private float getDrawingScale(View view) {
return mUseIncreasedIconScale && view instanceof StatusBarIconView
? ((StatusBarIconView) view).getIconScaleIncreased()
@@ -563,11 +567,21 @@
mUseIncreasedIconScale = useIncreasedIconScale;
}
- private float getLayoutEnd() {
+ /**
+ * @return The right boundary (not the RTL compatible end) of the area that icons can be added.
+ */
+ protected float getRightBound() {
return getActualWidth() - getActualPaddingEnd();
}
- private float getActualPaddingEnd() {
+ /**
+ * @return The left boundary (not the RTL compatible start) of the area that icons can be added.
+ */
+ protected float getLeftBound() {
+ return getActualPaddingStart();
+ }
+
+ protected float getActualPaddingEnd() {
if (mActualPaddingEnd == NO_VALUE) {
return getPaddingEnd();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
index ebf4391..c3299bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
@@ -19,6 +19,7 @@
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import android.widget.LinearLayout
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
@@ -28,13 +29,17 @@
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.platform.ComposeView
+import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.compose.ui.viewinterop.AndroidView
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.app.tracing.coroutines.launchTraced as launch
+import com.android.keyguard.AlphaOptimizedLinearLayout
import com.android.systemui.plugins.DarkIconDispatcher
import com.android.systemui.res.R
import com.android.systemui.statusbar.data.repository.DarkIconDispatcherStore
import com.android.systemui.statusbar.events.domain.interactor.SystemStatusEventAnimationInteractor
+import com.android.systemui.statusbar.featurepods.popups.StatusBarPopupChips
+import com.android.systemui.statusbar.featurepods.popups.ui.compose.StatusBarPopupChipsContainer
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerStatusBarViewBinder
import com.android.systemui.statusbar.phone.NotificationIconContainer
import com.android.systemui.statusbar.phone.PhoneStatusBarView
@@ -172,6 +177,35 @@
R.id.notificationIcons
)
+ // Add a composable container for `StatusBarPopupChip`s
+ if (StatusBarPopupChips.isEnabled) {
+ val endSideContent =
+ phoneStatusBarView.requireViewById<AlphaOptimizedLinearLayout>(
+ R.id.status_bar_end_side_content
+ )
+
+ val composeView =
+ ComposeView(context).apply {
+ layoutParams =
+ LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.WRAP_CONTENT,
+ LinearLayout.LayoutParams.WRAP_CONTENT,
+ )
+
+ setViewCompositionStrategy(
+ ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed
+ )
+
+ setContent {
+ val chips =
+ statusBarViewModel.statusBarPopupChips
+ .collectAsStateWithLifecycle()
+ StatusBarPopupChipsContainer(chips = chips.value)
+ }
+ }
+ endSideContent.addView(composeView, 0)
+ }
+
scope.launch {
notificationIconsBinder.bindWhileAttached(
notificationIconContainer,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt
index 7f9a80b..dcfbc5d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt
@@ -42,6 +42,8 @@
import com.android.systemui.statusbar.events.domain.interactor.SystemStatusEventAnimationInteractor
import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState
import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.Idle
+import com.android.systemui.statusbar.featurepods.popups.shared.model.PopupChipModel
+import com.android.systemui.statusbar.featurepods.popups.ui.viewmodel.StatusBarPopupChipsViewModel
import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationInteractor
import com.android.systemui.statusbar.notification.headsup.PinnedStatus
@@ -99,6 +101,9 @@
/** View model for the carrier name that may show in the status bar based on carrier config */
val operatorNameViewModel: StatusBarOperatorNameViewModel
+ /** The popup chips that should be shown on the right-hand side of the status bar. */
+ val statusBarPopupChips: StateFlow<List<PopupChipModel.Shown>>
+
/**
* True if the current scene can show the home status bar (aka this status bar), and false if
* the current scene should never show the home status bar.
@@ -170,6 +175,7 @@
sceneContainerOcclusionInteractor: SceneContainerOcclusionInteractor,
shadeInteractor: ShadeInteractor,
ongoingActivityChipsViewModel: OngoingActivityChipsViewModel,
+ statusBarPopupChipsViewModel: StatusBarPopupChipsViewModel,
animations: SystemStatusEventAnimationInteractor,
@Application coroutineScope: CoroutineScope,
) : HomeStatusBarViewModel {
@@ -188,6 +194,8 @@
override val ongoingActivityChips = ongoingActivityChipsViewModel.chips
+ override val statusBarPopupChips = statusBarPopupChipsViewModel.shownPopupChips
+
override val isHomeStatusBarAllowedByScene: StateFlow<Boolean> =
combine(
sceneInteractor.currentScene,
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 a3dcc3b..ece5a3f 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 @@
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 52f80fb..ab20850 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 @@
}
@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/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
index 84976a9..0b2b867 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
@@ -32,8 +32,8 @@
import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.dock.DockManagerFake
-import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.quickaffordance.BuiltInKeyguardQuickAffordanceKeys
import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceProviderClientFactory
@@ -191,10 +191,7 @@
dockManager = DockManagerFake()
biometricSettingsRepository = FakeBiometricSettingsRepository()
- val featureFlags =
- FakeFeatureFlags().apply { set(Flags.LOCK_SCREEN_LONG_PRESS_ENABLED, false) }
-
- val withDeps = KeyguardInteractorFactory.create(featureFlags = featureFlags)
+ val withDeps = KeyguardInteractorFactory.create()
keyguardInteractor = withDeps.keyguardInteractor
repository = withDeps.repository
@@ -287,7 +284,7 @@
keyguardStateController = keyguardStateController,
userTracker = userTracker,
activityStarter = activityStarter,
- featureFlags = featureFlags,
+ featureFlags = kosmos.fakeFeatureFlagsClassic,
repository = { quickAffordanceRepository },
launchAnimator = launchAnimator,
logger = logger,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
index 47371df..23282b1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
@@ -20,6 +20,7 @@
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -27,6 +28,8 @@
import android.app.KeyguardManager;
import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.media.AudioManager;
import android.media.session.MediaController;
@@ -38,6 +41,7 @@
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
+import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.core.graphics.drawable.IconCompat;
@@ -128,6 +132,21 @@
when(mMediaController.getPackageName()).thenReturn(TEST_PACKAGE);
mMediaControllers.add(mMediaController);
when(mMediaSessionManager.getActiveSessions(any())).thenReturn(mMediaControllers);
+ createMediaSwitchingController(TEST_PACKAGE);
+
+ // Using a fake package will cause routing operations to fail, so we intercept
+ // scanning-related operations.
+ mMediaSwitchingController.mLocalMediaManager = mock(LocalMediaManager.class);
+ doNothing().when(mMediaSwitchingController.mLocalMediaManager).startScan();
+ doNothing().when(mMediaSwitchingController.mLocalMediaManager).stopScan();
+
+ mMediaOutputBaseDialogImpl =
+ new MediaOutputBaseDialogImpl(
+ mContext, mBroadcastSender, mMediaSwitchingController);
+ mMediaOutputBaseDialogImpl.onCreate(new Bundle());
+ }
+
+ private void createMediaSwitchingController(String testPackage) {
VolumePanelGlobalStateInteractor volumePanelGlobalStateInteractor =
VolumePanelGlobalStateInteractorKosmosKt.getVolumePanelGlobalStateInteractor(
mKosmos);
@@ -135,7 +154,7 @@
mMediaSwitchingController =
new MediaSwitchingController(
mContext,
- TEST_PACKAGE,
+ testPackage,
mContext.getUser(),
/* token */ null,
mMediaSessionManager,
@@ -150,17 +169,40 @@
mFlags,
volumePanelGlobalStateInteractor,
mUserTracker);
+ }
- // Using a fake package will cause routing operations to fail, so we intercept
- // scanning-related operations.
- mMediaSwitchingController.mLocalMediaManager = mock(LocalMediaManager.class);
- doNothing().when(mMediaSwitchingController.mLocalMediaManager).startScan();
- doNothing().when(mMediaSwitchingController.mLocalMediaManager).stopScan();
+ @Test
+ public void onCreate_noAppOpenIntent_metadataSectionNonClickable() {
+ createMediaSwitchingController(null);
mMediaOutputBaseDialogImpl =
new MediaOutputBaseDialogImpl(
mContext, mBroadcastSender, mMediaSwitchingController);
mMediaOutputBaseDialogImpl.onCreate(new Bundle());
+ final LinearLayout mediaMetadataSectionLayout =
+ mMediaOutputBaseDialogImpl.mDialogView.requireViewById(
+ R.id.media_metadata_section);
+
+ assertThat(mediaMetadataSectionLayout.isClickable()).isFalse();
+ }
+
+ @Test
+ public void onCreate_appOpenIntentAvailable_metadataSectionClickable() {
+ final PackageManager packageManager = mock(PackageManager.class);
+ mContext.setMockPackageManager(packageManager);
+ Intent intent = new Intent(TEST_PACKAGE);
+ doReturn(intent).when(packageManager).getLaunchIntentForPackage(TEST_PACKAGE);
+ createMediaSwitchingController(TEST_PACKAGE);
+
+ mMediaOutputBaseDialogImpl =
+ new MediaOutputBaseDialogImpl(
+ mContext, mBroadcastSender, mMediaSwitchingController);
+ mMediaOutputBaseDialogImpl.onCreate(new Bundle());
+ final LinearLayout mediaMetadataSectionLayout =
+ mMediaOutputBaseDialogImpl.mDialogView.requireViewById(
+ R.id.media_metadata_section);
+
+ assertThat(mediaMetadataSectionLayout.isClickable()).isTrue();
}
@Test
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 afff485..a17f100 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.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 @@
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/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 192d66c..7508838 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.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.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;
@@ -197,6 +198,7 @@
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.taskview.TaskView;
+import com.android.wm.shell.taskview.TaskViewRepository;
import com.android.wm.shell.taskview.TaskViewTransitions;
import com.android.wm.shell.transition.Transitions;
@@ -325,7 +327,9 @@
@Mock
private LauncherApps mLauncherApps;
@Mock
- private WindowManagerShellWrapper mWindowManagerShellWrapper;
+ private DisplayInsetsController mDisplayInsetsController;
+ @Mock
+ private DisplayImeController mDisplayImeController;
@Mock
private BubbleLogger mBubbleLogger;
@Mock
@@ -359,6 +363,7 @@
private ShadeInteractor mShadeInteractor;
private NotificationShadeWindowModel mNotificationShadeWindowModel;
private ShellTaskOrganizer mShellTaskOrganizer;
+ private TaskViewRepository mTaskViewRepository;
private TaskViewTransitions mTaskViewTransitions;
private TestableBubblePositioner mPositioner;
@@ -395,7 +400,8 @@
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
doReturn(true).when(mTransitions).isRegistered();
}
- mTaskViewTransitions = new TaskViewTransitions(mTransitions);
+ mTaskViewRepository = new TaskViewRepository();
+ mTaskViewTransitions = new TaskViewTransitions(mTransitions, mTaskViewRepository);
mTestableLooper = TestableLooper.get(this);
@@ -503,7 +509,7 @@
mContext,
mock(NotificationManager.class),
mock(NotificationSettingsInteractor.class)
- );
+ );
interruptionDecisionProvider.start();
mShellTaskOrganizer = new ShellTaskOrganizer(mock(ShellInit.class),
@@ -523,7 +529,8 @@
mDataRepository,
mStatusBarService,
mWindowManager,
- mWindowManagerShellWrapper,
+ mDisplayInsetsController,
+ mDisplayImeController,
mUserManager,
mLauncherApps,
mBubbleLogger,
@@ -1430,9 +1437,12 @@
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/flags/FeatureFlagsClassicKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsClassicKosmos.kt
index d9235cc..2f7936a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsClassicKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsClassicKosmos.kt
@@ -34,7 +34,6 @@
Kosmos.Fixture {
FakeFeatureFlagsClassic().apply {
set(Flags.FULL_SCREEN_USER_SWITCHER, false)
- set(Flags.LOCK_SCREEN_LONG_PRESS_ENABLED, false)
set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false)
set(Flags.NSSL_DEBUG_LINES, false)
set(Flags.COMMUNAL_SERVICE_ENABLED, false)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/util/FakeMediaControllerFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/util/FakeMediaControllerFactory.kt
index b833750..b20678e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/util/FakeMediaControllerFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/util/FakeMediaControllerFactory.kt
@@ -35,7 +35,7 @@
return mediaControllersForToken[token]!!
}
- override suspend fun create(token: SessionToken, looper: Looper): Media3Controller {
+ override suspend fun create(token: SessionToken, looper: Looper): Media3Controller? {
return media3Controller ?: super.create(token, looper)
}
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 8aa7a03..d5637cb 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 @@
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 30b4763..4c9e174 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 @@
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/featurepods/media/domain/interactor/MediaControlChipInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/featurepods/media/domain/interactor/MediaControlChipInteractorKosmos.kt
index 0025ad4..f7e235a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/featurepods/media/domain/interactor/MediaControlChipInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/featurepods/media/domain/interactor/MediaControlChipInteractorKosmos.kt
@@ -17,13 +17,13 @@
package com.android.systemui.statusbar.featurepods.media.domain.interactor
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.backgroundScope
import com.android.systemui.media.controls.data.repository.mediaFilterRepository
val Kosmos.mediaControlChipInteractor: MediaControlChipInteractor by
Kosmos.Fixture {
MediaControlChipInteractor(
- applicationScope = applicationCoroutineScope,
+ backgroundScope = backgroundScope,
mediaFilterRepository = mediaFilterRepository,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/featurepods/media/ui/viewmodel/MediaControlChipViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/featurepods/media/ui/viewmodel/MediaControlChipViewModelKosmos.kt
new file mode 100644
index 0000000..7145907
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/featurepods/media/ui/viewmodel/MediaControlChipViewModelKosmos.kt
@@ -0,0 +1,31 @@
+/*
+ * 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.featurepods.media.ui.viewmodel
+
+import android.content.testableContext
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.statusbar.featurepods.media.domain.interactor.mediaControlChipInteractor
+
+val Kosmos.mediaControlChipViewModel: MediaControlChipViewModel by
+ Kosmos.Fixture {
+ MediaControlChipViewModel(
+ backgroundScope = applicationCoroutineScope,
+ applicationContext = testableContext,
+ mediaControlChipInteractor = mediaControlChipInteractor,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModelKosmos.kt
index 62cdc87..93502f3 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModelKosmos.kt
@@ -18,6 +18,12 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
+import com.android.systemui.statusbar.featurepods.media.ui.viewmodel.mediaControlChipViewModel
val Kosmos.statusBarPopupChipsViewModel: StatusBarPopupChipsViewModel by
- Kosmos.Fixture { StatusBarPopupChipsViewModel(testScope.backgroundScope) }
+ Kosmos.Fixture {
+ StatusBarPopupChipsViewModel(
+ testScope.backgroundScope,
+ mediaControlChipViewModel = mediaControlChipViewModel,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt
index 924b6b4..b38a723 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt
@@ -25,6 +25,7 @@
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.statusbar.chips.ui.viewmodel.ongoingActivityChipsViewModel
import com.android.systemui.statusbar.events.domain.interactor.systemStatusEventAnimationInteractor
+import com.android.systemui.statusbar.featurepods.popups.ui.viewmodel.statusBarPopupChipsViewModel
import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.headsUpNotificationInteractor
import com.android.systemui.statusbar.phone.domain.interactor.darkIconInteractor
@@ -48,6 +49,7 @@
sceneContainerOcclusionInteractor,
shadeInteractor,
ongoingActivityChipsViewModel,
+ statusBarPopupChipsViewModel,
systemStatusEventAnimationInteractor,
applicationCoroutineScope,
)
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 2df0c7a5..da6b2ae 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 @@
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 2249bc0..857dc85 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 @@
}
@Override
- public void stopCasting(CastDevice device) {
+ public void stopCasting(CastDevice device, @StopReason int stopReason) {
}
diff --git a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
index 669025f..37276dd 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 @@
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 @@
}
}
+ /**
+ * 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 7ba1bbc..666d8fe 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.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 Context mContext;
+ private final PackageManager mPackageManager;
+ private final Executor mLoggingExecutor;
+ private final AppFunctionsLoggerClock mLoggerClock;
- public AppFunctionsLoggerWrapper(@NonNull Context context) {
- mContext = Objects.requireNonNull(context);
+ AppFunctionsLoggerWrapper(@NonNull Context context) {
+ this(context.getPackageManager(), LOGGING_THREAD_EXECUTOR, SystemClock::elapsedRealtime);
}
- void logAppFunctionSuccess(ExecuteAppFunctionAidlRequest request,
- ExecuteAppFunctionResponse response, int callingUid, long executionStartTimeMillis) {
- logAppFunctionsRequestReported(request, SUCCESS_RESPONSE_CODE,
- response.getResponseDataSize(), callingUid, executionStartTimeMillis);
+ @VisibleForTesting
+ AppFunctionsLoggerWrapper(
+ @NonNull PackageManager packageManager,
+ @NonNull Executor executor,
+ AppFunctionsLoggerClock loggerClock) {
+ mLoggingExecutor = Objects.requireNonNull(executor);
+ mPackageManager = Objects.requireNonNull(packageManager);
+ mLoggerClock = loggerClock;
}
- void logAppFunctionError(ExecuteAppFunctionAidlRequest request, int errorCode, int callingUid,
+ void logAppFunctionSuccess(
+ ExecuteAppFunctionAidlRequest request,
+ ExecuteAppFunctionResponse response,
+ int callingUid,
long executionStartTimeMillis) {
- logAppFunctionsRequestReported(request, errorCode, /* responseSizeBytes = */ 0, callingUid,
+ logAppFunctionsRequestReported(
+ request,
+ SUCCESS_RESPONSE_CODE,
+ response.getResponseDataSize(),
+ callingUid,
executionStartTimeMillis);
}
- private void logAppFunctionsRequestReported(ExecuteAppFunctionAidlRequest request,
- int errorCode, int responseSizeBytes, int callingUid, long executionStartTimeMillis) {
+ void logAppFunctionError(
+ ExecuteAppFunctionAidlRequest request,
+ int errorCode,
+ int callingUid,
+ long executionStartTimeMillis) {
+ logAppFunctionsRequestReported(
+ request,
+ errorCode,
+ /* responseSizeBytes= */ 0,
+ callingUid,
+ 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 a5ae7e3..4cba8ec 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.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 @@
@NonNull IAppFunctionService service,
@NonNull ServiceUsageCompleteListener serviceUsageCompleteListener) {
try {
- mSafeExecuteAppFunctionCallback.setExecutionStartTimeMillis();
+ mSafeExecuteAppFunctionCallback.setExecutionStartTimeAfterBindMillis(
+ SystemClock.elapsedRealtime());
service.executeAppFunction(
mRequestInternal.getClientRequest(),
mRequestInternal.getCallingPackage(),
@@ -73,8 +75,7 @@
} 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 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/backup/java/com/android/server/backup/BackupManagerConstants.java b/services/backup/java/com/android/server/backup/BackupManagerConstants.java
index 4bd987a..b753d01 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerConstants.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerConstants.java
@@ -72,6 +72,9 @@
public static final String BACKUP_FINISHED_NOTIFICATION_RECEIVERS =
"backup_finished_notification_receivers";
+ @VisibleForTesting
+ public static final String WAKELOCK_TIMEOUT_MILLIS = "wakelock_timeout_millis";
+
// Hard coded default values.
@VisibleForTesting
public static final long DEFAULT_KEY_VALUE_BACKUP_INTERVAL_MILLISECONDS =
@@ -97,6 +100,9 @@
@VisibleForTesting
public static final String DEFAULT_BACKUP_FINISHED_NOTIFICATION_RECEIVERS = "";
+ @VisibleForTesting
+ public static final long DEFAULT_WAKELOCK_TIMEOUT_MILLIS = 30 * 60 * 1000; // 30 minutes
+
// Backup manager constants.
private long mKeyValueBackupIntervalMilliseconds;
private long mKeyValueBackupFuzzMilliseconds;
@@ -106,6 +112,7 @@
private boolean mFullBackupRequireCharging;
private int mFullBackupRequiredNetworkType;
private String[] mBackupFinishedNotificationReceivers;
+ private long mWakelockTimeoutMillis;
public BackupManagerConstants(Handler handler, ContentResolver resolver) {
super(handler, resolver, Settings.Secure.getUriFor(SETTING));
@@ -152,6 +159,8 @@
} else {
mBackupFinishedNotificationReceivers = backupFinishedNotificationReceivers.split(":");
}
+ mWakelockTimeoutMillis = parser.getLong(WAKELOCK_TIMEOUT_MILLIS,
+ DEFAULT_WAKELOCK_TIMEOUT_MILLIS);
}
// The following are access methods for the individual parameters.
@@ -235,4 +244,9 @@
}
return mBackupFinishedNotificationReceivers;
}
+
+ public synchronized long getWakelockTimeoutMillis() {
+ Slog.v(TAG, "wakelock timeout: " + mWakelockTimeoutMillis);
+ return mWakelockTimeoutMillis;
+ }
}
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 549f8fa..ac1f50f 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -181,11 +181,14 @@
public static class BackupWakeLock {
private final PowerManager.WakeLock mPowerManagerWakeLock;
private boolean mHasQuit = false;
- private int mUserId;
+ private final int mUserId;
+ private final BackupManagerConstants mBackupManagerConstants;
- public BackupWakeLock(PowerManager.WakeLock powerManagerWakeLock, int userId) {
+ public BackupWakeLock(PowerManager.WakeLock powerManagerWakeLock, int userId,
+ BackupManagerConstants backupManagerConstants) {
mPowerManagerWakeLock = powerManagerWakeLock;
mUserId = userId;
+ mBackupManagerConstants = backupManagerConstants;
}
/** Acquires the {@link PowerManager.WakeLock} if hasn't been quit. */
@@ -199,7 +202,9 @@
+ mPowerManagerWakeLock.getTag()));
return;
}
- mPowerManagerWakeLock.acquire();
+ // Set a timeout for the wakelock. Otherwise if we fail internally and never call
+ // release(), the device might stay awake and drain battery indefinitely.
+ mPowerManagerWakeLock.acquire(mBackupManagerConstants.getWakelockTimeoutMillis());
Slog.v(
TAG,
addUserIdToLogMessage(
@@ -674,10 +679,8 @@
mBackupPreferences = new UserBackupPreferences(mContext, mBaseStateDir);
// Power management
- mWakelock = new BackupWakeLock(
- mPowerManager.newWakeLock(
- PowerManager.PARTIAL_WAKE_LOCK,
- "*backup*-" + userId + "-" + userBackupThread.getThreadId()), userId);
+ mWakelock = new BackupWakeLock(mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+ "*backup*-" + userId + "-" + userBackupThread.getThreadId()), userId, mConstants);
// Set up the various sorts of package tracking we do
mFullBackupScheduleFile = new File(mBaseStateDir, "fb-schedule");
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 a1d621d..6bf60bf 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 @@
// 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());
+ 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());
+ 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/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index a8d5696..c384b54 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 @@
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/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 286333c..4c959fa 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -1087,16 +1087,10 @@
return null;
}
- final AndroidFuture<Intent[]> ret = new AndroidFuture<>();
- Intent[] intents;
- mShortcutServiceInternal.createShortcutIntentsAsync(getCallingUserId(),
- callingPackage, packageName, shortcutId, user.getIdentifier(),
- injectBinderCallingPid(), injectBinderCallingUid(), ret);
- try {
- intents = ret.get();
- } catch (InterruptedException | ExecutionException e) {
- return null;
- }
+ Intent[] intents = mShortcutServiceInternal.createShortcutIntents(
+ getCallingUserId(), callingPackage, packageName, shortcutId,
+ user.getIdentifier(), injectBinderCallingPid(),
+ injectBinderCallingUid());
if (intents == null || intents.length == 0) {
return null;
}
@@ -1275,40 +1269,6 @@
}
@Override
- public void getShortcutsAsync(@NonNull final String callingPackage,
- @NonNull final ShortcutQueryWrapper query, @NonNull final UserHandle targetUser,
- @NonNull final AndroidFuture<List<ShortcutInfo>> cb) {
- ensureShortcutPermission(callingPackage);
- if (!canAccessProfile(targetUser.getIdentifier(), "Cannot get shortcuts")) {
- cb.complete(Collections.EMPTY_LIST);
- return;
- }
-
- final long changedSince = query.getChangedSince();
- final String packageName = query.getPackage();
- final List<String> shortcutIds = query.getShortcutIds();
- final List<LocusId> locusIds = query.getLocusIds();
- final ComponentName componentName = query.getActivity();
- final int flags = query.getQueryFlags();
- if (shortcutIds != null && packageName == null) {
- throw new IllegalArgumentException(
- "To query by shortcut ID, package name must also be set");
- }
- if (locusIds != null && packageName == null) {
- throw new IllegalArgumentException(
- "To query by locus ID, package name must also be set");
- }
- if ((query.getQueryFlags() & ShortcutQuery.FLAG_GET_PERSONS_DATA) != 0) {
- ensureStrictAccessShortcutsPermission(callingPackage);
- }
-
- mShortcutServiceInternal.getShortcutsAsync(getCallingUserId(),
- callingPackage, changedSince, packageName, shortcutIds, locusIds,
- componentName, flags, targetUser.getIdentifier(),
- injectBinderCallingPid(), injectBinderCallingUid(), cb);
- }
-
- @Override
public void registerShortcutChangeCallback(@NonNull final String callingPackage,
@NonNull final ShortcutQueryWrapper query,
@NonNull final IShortcutChangeCallback callback) {
@@ -1406,15 +1366,8 @@
if (!canAccessProfile(targetUserId, "Cannot access shortcuts")) {
return null;
}
-
- final AndroidFuture<ParcelFileDescriptor> ret = new AndroidFuture<>();
- mShortcutServiceInternal.getShortcutIconFdAsync(getCallingUserId(),
- callingPackage, packageName, id, targetUserId, ret);
- try {
- return ret.get();
- } catch (InterruptedException | ExecutionException e) {
- throw new RuntimeException(e);
- }
+ return mShortcutServiceInternal.getShortcutIconFd(getCallingUserId(),
+ callingPackage, packageName, id, targetUserId);
}
@Override
@@ -1424,15 +1377,9 @@
if (!canAccessProfile(userId, "Cannot access shortcuts")) {
return null;
}
-
- final AndroidFuture<String> ret = new AndroidFuture<>();
- mShortcutServiceInternal.getShortcutIconUriAsync(getCallingUserId(), callingPackage,
- packageName, shortcutId, userId, ret);
- try {
- return ret.get();
- } catch (InterruptedException | ExecutionException e) {
- throw new RuntimeException(e);
- }
+ return mShortcutServiceInternal.getShortcutIconUri(
+ getCallingUserId(), callingPackage,
+ packageName, shortcutId, userId);
}
@Override
@@ -1515,16 +1462,9 @@
ensureShortcutPermission(callerUid, callerPid, callingPackage);
}
- final AndroidFuture<Intent[]> ret = new AndroidFuture<>();
- Intent[] intents;
- mShortcutServiceInternal.createShortcutIntentsAsync(getCallingUserId(), callingPackage,
- packageName, shortcutId, targetUserId,
- injectBinderCallingPid(), injectBinderCallingUid(), ret);
- try {
- intents = ret.get();
- } catch (InterruptedException | ExecutionException e) {
- return false;
- }
+ Intent[] intents = mShortcutServiceInternal.createShortcutIntents(
+ getCallingUserId(), callingPackage, packageName, shortcutId,
+ targetUserId, injectBinderCallingPid(), injectBinderCallingUid());
if (intents == null || intents.length == 0) {
return false;
}
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 6f50295..f140400 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -19,27 +19,11 @@
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.Person;
-import android.app.appsearch.AppSearchBatchResult;
-import android.app.appsearch.AppSearchManager;
-import android.app.appsearch.AppSearchResult;
-import android.app.appsearch.AppSearchSession;
-import android.app.appsearch.BatchResultCallback;
-import android.app.appsearch.GenericDocument;
-import android.app.appsearch.GetByDocumentIdRequest;
-import android.app.appsearch.PutDocumentsRequest;
-import android.app.appsearch.RemoveByDocumentIdRequest;
-import android.app.appsearch.ReportUsageRequest;
-import android.app.appsearch.SearchResult;
-import android.app.appsearch.SearchResults;
-import android.app.appsearch.SearchSpec;
-import android.app.appsearch.SetSchemaRequest;
import android.app.usage.UsageStatsManagerInternal;
import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.LocusId;
-import android.content.pm.AppSearchShortcutInfo;
-import android.content.pm.AppSearchShortcutPerson;
import android.content.pm.PackageInfo;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager;
@@ -58,7 +42,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.infra.AndroidFuture;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
@@ -167,13 +150,6 @@
private final ArrayMap<String, ShortcutInfo> mShortcuts = new ArrayMap<>();
/**
- * A temporary copy of shortcuts that are to be cleared once persisted into AppSearch, keyed on
- * IDs.
- */
- @GuardedBy("mPackageItemLock")
- private final ArrayMap<String, ShortcutInfo> mTransientShortcuts = new ArrayMap<>(0);
-
- /**
* All the share targets from the package
*/
@GuardedBy("mPackageItemLock")
@@ -196,9 +172,6 @@
@GuardedBy("mPackageItemLock")
private long mLastReportedTime;
- @GuardedBy("mPackageItemLock")
- private boolean mIsAppSearchSchemaUpToDate;
-
private ShortcutPackage(ShortcutUser shortcutUser,
int packageUserId, String packageName, ShortcutPackageInfo spi) {
super(shortcutUser, packageUserId, packageName,
@@ -228,10 +201,6 @@
getPackageName(), getPackageUserId());
}
- private boolean isAppSearchEnabled() {
- return mShortcutUser.mService.isAppSearchEnabled();
- }
-
public int getShortcutCount() {
synchronized (mPackageItemLock) {
return mShortcuts.size();
@@ -249,9 +218,6 @@
// - Unshadow all shortcuts.
// - Set disabled reason.
// - Disable if needed.
- final String query = String.format("%s:-%s AND %s:%s",
- AppSearchShortcutInfo.KEY_FLAGS, ShortcutInfo.FLAG_SHADOW,
- AppSearchShortcutInfo.KEY_DISABLED_REASON, restoreBlockReason);
forEachShortcutMutate(si -> {
if (restoreBlockReason == ShortcutInfo.DISABLED_REASON_NOT_DISABLED
&& !si.hasFlags(ShortcutInfo.FLAG_SHADOW)
@@ -407,13 +373,7 @@
& (ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_CACHED_ALL));
}
- if (newShortcut.isExcludedFromSurfaces(ShortcutInfo.SURFACE_LAUNCHER)) {
- if (isAppSearchEnabled()) {
- synchronized (mPackageItemLock) {
- mTransientShortcuts.put(newShortcut.getId(), newShortcut);
- }
- }
- } else {
+ if (!newShortcut.isExcludedFromSurfaces(ShortcutInfo.SURFACE_LAUNCHER)) {
forceReplaceShortcutInner(newShortcut);
}
return oldShortcut != null;
@@ -466,8 +426,7 @@
}
changedShortcuts.add(shortcut);
- deleted = deleteDynamicWithId(shortcut.getId(), /* ignoreInvisible =*/ true,
- /*ignorePersistedShortcuts=*/ true) != null;
+ deleted = deleteDynamicWithId(shortcut.getId(), /* ignoreInvisible =*/ true) != null;
}
}
if (oldShortcut != null) {
@@ -480,25 +439,9 @@
& (ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_CACHED_ALL));
}
- if (newShortcut.isExcludedFromSurfaces(ShortcutInfo.SURFACE_LAUNCHER)) {
- if (isAppSearchEnabled()) {
- synchronized (mPackageItemLock) {
- mTransientShortcuts.put(newShortcut.getId(), newShortcut);
- }
- }
- } else {
+ if (!newShortcut.isExcludedFromSurfaces(ShortcutInfo.SURFACE_LAUNCHER)) {
forceReplaceShortcutInner(newShortcut);
}
- if (isAppSearchEnabled()) {
- runAsSystem(() -> fromAppSearch().thenAccept(session ->
- session.reportUsage(new ReportUsageRequest.Builder(
- getPackageName(), newShortcut.getId()).build(), mExecutor, result -> {
- if (!result.isSuccess()) {
- Slog.e(TAG, "Failed to report usage via AppSearch. "
- + result.getErrorMessage());
- }
- })));
- }
return deleted;
}
@@ -567,7 +510,6 @@
}
}
}
- removeAllShortcutsAsync();
if (changed) {
return removeOrphans();
}
@@ -581,11 +523,10 @@
* @return The deleted shortcut, or null if it was not actually removed because it is either
* pinned or cached.
*/
- public ShortcutInfo deleteDynamicWithId(@NonNull String shortcutId, boolean ignoreInvisible,
- boolean ignorePersistedShortcuts) {
+ public ShortcutInfo deleteDynamicWithId(@NonNull String shortcutId, boolean ignoreInvisible) {
return deleteOrDisableWithId(
shortcutId, /* disable =*/ false, /* overrideImmutable=*/ false, ignoreInvisible,
- ShortcutInfo.DISABLED_REASON_NOT_DISABLED, ignorePersistedShortcuts);
+ ShortcutInfo.DISABLED_REASON_NOT_DISABLED);
}
/**
@@ -596,9 +537,9 @@
* it's still pinned.
*/
private ShortcutInfo disableDynamicWithId(@NonNull String shortcutId, boolean ignoreInvisible,
- int disabledReason, boolean ignorePersistedShortcuts) {
+ int disabledReason) {
return deleteOrDisableWithId(shortcutId, /* disable =*/ true, /* overrideImmutable=*/ false,
- ignoreInvisible, disabledReason, ignorePersistedShortcuts);
+ ignoreInvisible, disabledReason);
}
/**
@@ -614,7 +555,7 @@
}
return deleteOrDisableWithId(
shortcutId, /* disable =*/ false, /* overrideImmutable=*/ false, ignoreInvisible,
- ShortcutInfo.DISABLED_REASON_NOT_DISABLED, /*ignorePersistedShortcuts=*/ false);
+ ShortcutInfo.DISABLED_REASON_NOT_DISABLED);
}
/**
@@ -628,8 +569,7 @@
int disabledMessageResId, boolean overrideImmutable, boolean ignoreInvisible,
int disabledReason) {
final ShortcutInfo deleted = deleteOrDisableWithId(shortcutId, /* disable =*/ true,
- overrideImmutable, ignoreInvisible, disabledReason,
- /*ignorePersistedShortcuts=*/ false);
+ overrideImmutable, ignoreInvisible, disabledReason);
// If disabled id still exists, it is pinned and we need to update the disabled message.
mutateShortcut(shortcutId, null, disabled -> {
@@ -648,8 +588,7 @@
@Nullable
private ShortcutInfo deleteOrDisableWithId(@NonNull String shortcutId, boolean disable,
- boolean overrideImmutable, boolean ignoreInvisible, int disabledReason,
- boolean ignorePersistedShortcuts) {
+ boolean overrideImmutable, boolean ignoreInvisible, int disabledReason) {
Preconditions.checkState(
(disable == (disabledReason != ShortcutInfo.DISABLED_REASON_NOT_DISABLED)),
"disable and disabledReason disagree: " + disable + " vs " + disabledReason);
@@ -662,9 +601,6 @@
if (!overrideImmutable) {
ensureNotImmutable(oldShortcut, /*ignoreInvisible=*/ true);
}
- if (!ignorePersistedShortcuts) {
- removeShortcutAsync(shortcutId);
- }
if (oldShortcut.isPinned() || oldShortcut.isCached()) {
mutateShortcut(oldShortcut.getId(), oldShortcut, si -> {
si.setRank(0);
@@ -1214,8 +1150,7 @@
"%s is no longer main activity. Disabling shorcut %s.",
getPackageName(), si.getId()));
if (disableDynamicWithId(si.getId(), /*ignoreInvisible*/ false,
- ShortcutInfo.DISABLED_REASON_APP_CHANGED,
- /*ignorePersistedShortcuts*/ false) != null) {
+ ShortcutInfo.DISABLED_REASON_APP_CHANGED) != null) {
return;
}
// Still pinned, so fall-through and possibly update the resources.
@@ -1367,8 +1302,7 @@
service.wtf("Found manifest shortcuts in excess list.");
continue;
}
- deleteDynamicWithId(shortcut.getId(), /*ignoreInvisible=*/ true,
- /*ignorePersistedShortcuts=*/ true);
+ deleteDynamicWithId(shortcut.getId(), /*ignoreInvisible=*/ true);
}
}
@@ -1818,15 +1752,7 @@
}
private boolean hasNoShortcut() {
- if (!isAppSearchEnabled()) {
- return getShortcutCount() == 0;
- }
- final boolean[] hasAnyShortcut = new boolean[1];
- forEachShortcutStopWhen(si -> {
- hasAnyShortcut[0] = true;
- return true;
- });
- return !hasAnyShortcut[0];
+ return getShortcutCount() == 0;
}
@Override
@@ -1845,10 +1771,6 @@
ShortcutService.writeAttr(out, ATTR_NAME, getPackageName());
ShortcutService.writeAttr(out, ATTR_CALL_COUNT, mApiCallCount);
ShortcutService.writeAttr(out, ATTR_LAST_RESET, mLastResetTime);
- if (!forBackup) {
- ShortcutService.writeAttr(out, ATTR_SCHEMA_VERSON, mIsAppSearchSchemaUpToDate
- ? AppSearchShortcutInfo.SCHEMA_VERSION : 0);
- }
getPackageInfo().saveToXml(mShortcutUser.mService, out, forBackup);
if (ShortcutService.DEBUG_REBOOT) {
@@ -2051,9 +1973,6 @@
final ShortcutPackage ret = new ShortcutPackage(shortcutUser,
shortcutUser.getUserId(), packageName);
synchronized (ret.mPackageItemLock) {
- ret.mIsAppSearchSchemaUpToDate = ShortcutService.parseIntAttribute(
- parser, ATTR_SCHEMA_VERSON, 0) == AppSearchShortcutInfo.SCHEMA_VERSION;
-
ret.mApiCallCount = ShortcutService.parseIntAttribute(parser, ATTR_CALL_COUNT);
ret.mLastResetTime = ShortcutService.parseLongAttribute(parser, ATTR_LAST_RESET);
@@ -2478,47 +2397,6 @@
}
}
- @NonNull
- private AndroidFuture<AppSearchSession> setupSchema(
- @NonNull final AppSearchSession session) {
- if (ShortcutService.DEBUG_REBOOT) {
- Slog.d(TAG, "Setup Schema for user=" + mShortcutUser.getUserId()
- + " pkg=" + getPackageName());
- }
- SetSchemaRequest.Builder schemaBuilder = new SetSchemaRequest.Builder()
- .addSchemas(AppSearchShortcutPerson.SCHEMA, AppSearchShortcutInfo.SCHEMA)
- .setForceOverride(true)
- .addRequiredPermissionsForSchemaTypeVisibility(AppSearchShortcutInfo.SCHEMA_TYPE,
- Collections.singleton(SetSchemaRequest.READ_HOME_APP_SEARCH_DATA))
- .addRequiredPermissionsForSchemaTypeVisibility(AppSearchShortcutInfo.SCHEMA_TYPE,
- Collections.singleton(SetSchemaRequest.READ_ASSISTANT_APP_SEARCH_DATA))
- .addRequiredPermissionsForSchemaTypeVisibility(AppSearchShortcutPerson.SCHEMA_TYPE,
- Collections.singleton(SetSchemaRequest.READ_HOME_APP_SEARCH_DATA))
- .addRequiredPermissionsForSchemaTypeVisibility(AppSearchShortcutPerson.SCHEMA_TYPE,
- Collections.singleton(SetSchemaRequest.READ_ASSISTANT_APP_SEARCH_DATA));
- final AndroidFuture<AppSearchSession> future = new AndroidFuture<>();
- session.setSchema(
- schemaBuilder.build(), mExecutor, mShortcutUser.mExecutor, result -> {
- if (!result.isSuccess()) {
- future.completeExceptionally(
- new IllegalArgumentException(result.getErrorMessage()));
- return;
- }
- future.complete(session);
- });
- return future;
- }
-
- @NonNull
- private SearchSpec getSearchSpec() {
- return new SearchSpec.Builder()
- .addFilterSchemas(AppSearchShortcutInfo.SCHEMA_TYPE)
- .addFilterNamespaces(getPackageName())
- .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
- .setResultCountPerPage(mShortcutUser.mService.getMaxActivityShortcuts())
- .build();
- }
-
private boolean verifyRanksSequential(List<ShortcutInfo> list) {
boolean failed = false;
@@ -2533,186 +2411,6 @@
return failed;
}
- // Async Operations
-
- /**
- * Removes all shortcuts from AppSearch.
- */
- void removeAllShortcutsAsync() {
- if (!isAppSearchEnabled()) {
- return;
- }
- runAsSystem(() -> fromAppSearch().thenAccept(session ->
- session.remove("", getSearchSpec(), mShortcutUser.mExecutor, result -> {
- if (!result.isSuccess()) {
- Slog.e(TAG, "Failed to remove shortcuts from AppSearch. "
- + result.getErrorMessage());
- }
- })));
- }
-
- void getShortcutByIdsAsync(@NonNull final Set<String> ids,
- @NonNull final Consumer<List<ShortcutInfo>> cb) {
- if (!isAppSearchEnabled()) {
- cb.accept(Collections.emptyList());
- return;
- }
- runAsSystem(() -> fromAppSearch().thenAccept(session -> {
- session.getByDocumentId(new GetByDocumentIdRequest.Builder(getPackageName())
- .addIds(ids).build(), mShortcutUser.mExecutor,
- new BatchResultCallback<String, GenericDocument>() {
- @Override
- public void onResult(
- @NonNull AppSearchBatchResult<String, GenericDocument> result) {
- final List<ShortcutInfo> ret = result.getSuccesses().values()
- .stream().map(doc ->
- ShortcutInfo.createFromGenericDocument(
- mShortcutUser.getUserId(), doc))
- .collect(Collectors.toList());
- cb.accept(ret);
- }
- @Override
- public void onSystemError(
- @Nullable Throwable throwable) {
- Slog.d(TAG, "Error retrieving shortcuts", throwable);
- }
- });
- }));
- }
-
- private void removeShortcutAsync(@NonNull final String... id) {
- Objects.requireNonNull(id);
- removeShortcutAsync(Arrays.asList(id));
- }
-
- private void removeShortcutAsync(@NonNull final Collection<String> ids) {
- if (!isAppSearchEnabled()) {
- return;
- }
- runAsSystem(() -> fromAppSearch().thenAccept(session ->
- session.remove(
- new RemoveByDocumentIdRequest.Builder(getPackageName()).addIds(ids).build(),
- mShortcutUser.mExecutor,
- new BatchResultCallback<String, Void>() {
- @Override
- public void onResult(
- @NonNull AppSearchBatchResult<String, Void> result) {
- if (!result.isSuccess()) {
- final Map<String, AppSearchResult<Void>> failures =
- result.getFailures();
- for (String key : failures.keySet()) {
- Slog.e(TAG, "Failed deleting " + key + ", error message:"
- + failures.get(key).getErrorMessage());
- }
- }
- }
- @Override
- public void onSystemError(@Nullable Throwable throwable) {
- Slog.e(TAG, "Error removing shortcuts", throwable);
- }
- })));
- }
-
- @GuardedBy("mPackageItemLock")
- @Override
- void scheduleSaveToAppSearchLocked() {
- final Map<String, ShortcutInfo> copy = new ArrayMap<>(mShortcuts);
- if (!mTransientShortcuts.isEmpty()) {
- copy.putAll(mTransientShortcuts);
- mTransientShortcuts.clear();
- }
- saveShortcutsAsync(copy.values().stream().filter(ShortcutInfo::usesQuota).collect(
- Collectors.toList()));
- }
-
- private void saveShortcutsAsync(
- @NonNull final Collection<ShortcutInfo> shortcuts) {
- Objects.requireNonNull(shortcuts);
- if (!isAppSearchEnabled() || shortcuts.isEmpty()) {
- // No need to invoke AppSearch when there's nothing to save.
- return;
- }
- if (ShortcutService.DEBUG_REBOOT) {
- Slog.d(TAG, "Saving shortcuts async for user=" + mShortcutUser.getUserId()
- + " pkg=" + getPackageName() + " ids=" + shortcuts.stream()
- .map(ShortcutInfo::getId).collect(Collectors.joining(",", "[", "]")));
- }
- runAsSystem(() -> fromAppSearch().thenAccept(session -> {
- if (shortcuts.isEmpty()) {
- return;
- }
- session.put(new PutDocumentsRequest.Builder()
- .addGenericDocuments(
- AppSearchShortcutInfo.toGenericDocuments(shortcuts))
- .build(),
- mShortcutUser.mExecutor,
- new BatchResultCallback<String, Void>() {
- @Override
- public void onResult(
- @NonNull AppSearchBatchResult<String, Void> result) {
- if (!result.isSuccess()) {
- for (AppSearchResult<Void> k : result.getFailures().values()) {
- Slog.e(TAG, k.getErrorMessage());
- }
- }
- }
- @Override
- public void onSystemError(@Nullable Throwable throwable) {
- Slog.d(TAG, "Error persisting shortcuts", throwable);
- }
- });
- }));
- }
-
- @VisibleForTesting
- void getTopShortcutsFromPersistence(AndroidFuture<List<ShortcutInfo>> cb) {
- if (!isAppSearchEnabled()) {
- cb.complete(null);
- }
- runAsSystem(() -> fromAppSearch().thenAccept(session -> {
- SearchResults res = session.search("", getSearchSpec());
- res.getNextPage(mShortcutUser.mExecutor, results -> {
- if (!results.isSuccess()) {
- cb.completeExceptionally(new IllegalStateException(results.getErrorMessage()));
- return;
- }
- cb.complete(results.getResultValue().stream()
- .map(SearchResult::getGenericDocument)
- .map(doc -> ShortcutInfo.createFromGenericDocument(
- mShortcutUser.getUserId(), doc))
- .collect(Collectors.toList()));
- });
- }));
- }
-
- @NonNull
- private AndroidFuture<AppSearchSession> fromAppSearch() {
- final StrictMode.ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
- final AppSearchManager.SearchContext searchContext =
- new AppSearchManager.SearchContext.Builder(getPackageName()).build();
- AndroidFuture<AppSearchSession> future = null;
- try {
- StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
- .detectAll()
- .penaltyLog() // TODO: change this to penaltyDeath to fix the call-site
- .build());
- future = mShortcutUser.getAppSearch(searchContext);
- synchronized (mPackageItemLock) {
- if (!mIsAppSearchSchemaUpToDate) {
- future = future.thenCompose(this::setupSchema);
- }
- mIsAppSearchSchemaUpToDate = true;
- }
- } catch (Exception e) {
- Slog.e(TAG, "Failed to create app search session. pkg="
- + getPackageName() + " user=" + mShortcutUser.getUserId(), e);
- Objects.requireNonNull(future).completeExceptionally(e);
- } finally {
- StrictMode.setThreadPolicy(oldPolicy);
- }
- return Objects.requireNonNull(future);
- }
-
private void runAsSystem(@NonNull final Runnable fn) {
final long callingIdentity = Binder.clearCallingIdentity();
try {
diff --git a/services/core/java/com/android/server/pm/ShortcutPackageItem.java b/services/core/java/com/android/server/pm/ShortcutPackageItem.java
index dfd2e08..44789e4 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackageItem.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackageItem.java
@@ -187,11 +187,6 @@
}
}
- @GuardedBy("mPackageItemLock")
- void scheduleSaveToAppSearchLocked() {
-
- }
-
public JSONObject dumpCheckin(boolean clear) throws JSONException {
final JSONObject result = new JSONObject();
result.put(KEY_NAME, mPackageName);
@@ -221,10 +216,7 @@
}
synchronized (mPackageItemLock) {
path.getParentFile().mkdirs();
- // TODO: Since we are persisting shortcuts into AppSearch, we should read from/write to
- // AppSearch as opposed to maintaining a separate XML file.
saveToFileLocked(path, false /*forBackup*/);
- scheduleSaveToAppSearchLocked();
}
}
diff --git a/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java b/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java
index 47a140a..c5899ae 100644
--- a/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java
+++ b/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java
@@ -521,8 +521,7 @@
if (DEBUG) {
Slog.d(TAG, "Removing " + shortcutId + " as dynamic");
}
- ps.deleteDynamicWithId(shortcutId, /*ignoreInvisible=*/ false,
- /*wasPushedOut=*/ false);
+ ps.deleteDynamicWithId(shortcutId, /*ignoreInvisible=*/ false);
}
ps.adjustRanks(); // Shouldn't be needed, but just in case.
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 1052c94..2785da5 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -113,7 +113,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
-import com.android.internal.infra.AndroidFuture;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.DumpUtils;
@@ -475,8 +474,6 @@
@GuardedBy("mServiceLock")
private final MetricsLogger mMetricsLogger = new MetricsLogger();
- private final boolean mIsAppSearchEnabled;
-
private ComponentName mChooserActivity;
static class InvalidFileFormatException extends Exception {
@@ -519,9 +516,6 @@
mShortcutRequestPinProcessor = new ShortcutRequestPinProcessor(this, mServiceLock);
mShortcutDumpFiles = new ShortcutDumpFiles(this);
- mIsAppSearchEnabled = DeviceConfig.getBoolean(NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.SHORTCUT_APPSEARCH_INTEGRATION, false)
- && !injectIsLowRamDevice();
if (onlyForPackageManagerApis) {
return; // Don't do anything further. For unit tests only.
@@ -559,10 +553,6 @@
injectRegisterRoleHoldersListener(mOnRoleHoldersChangedListener);
}
- boolean isAppSearchEnabled() {
- return mIsAppSearchEnabled;
- }
-
long getStatStartTime() {
return mStatLogger.getTime();
}
@@ -757,8 +747,6 @@
if (DEBUG || DEBUG_REBOOT) {
Slog.d(TAG, "unloadUserLocked: userId=" + userId);
}
- // Cancel any ongoing background tasks.
- getUserShortcutsLocked(userId).cancelAllInFlightTasks();
// Save all dirty information.
saveDirtyInfo();
@@ -2298,20 +2286,20 @@
}
@Override
- public void requestPinShortcut(String packageName, ShortcutInfo shortcut,
- IntentSender resultIntent, int userId, AndroidFuture<String> ret) {
+ public boolean requestPinShortcut(String packageName, ShortcutInfo shortcut,
+ IntentSender resultIntent, int userId) {
Objects.requireNonNull(shortcut);
Preconditions.checkArgument(shortcut.isEnabled(), "Shortcut must be enabled");
Preconditions.checkArgument(
!shortcut.isExcludedFromSurfaces(ShortcutInfo.SURFACE_LAUNCHER),
"Shortcut excluded from launcher cannot be pinned");
- ret.complete(String.valueOf(requestPinItem(
- packageName, userId, shortcut, null, null, resultIntent)));
+ return requestPinItem(
+ packageName, userId, shortcut, null, null, resultIntent);
}
@Override
- public void createShortcutResultIntent(String packageName, ShortcutInfo shortcut, int userId,
- AndroidFuture<Intent> ret) throws RemoteException {
+ public Intent createShortcutResultIntent(String packageName,
+ ShortcutInfo shortcut, int userId) throws RemoteException {
Objects.requireNonNull(shortcut);
Preconditions.checkArgument(shortcut.isEnabled(), "Shortcut must be enabled");
verifyCaller(packageName, userId);
@@ -2323,7 +2311,7 @@
intent = mShortcutRequestPinProcessor.createShortcutResultIntent(shortcut, userId);
}
verifyStates();
- ret.complete(intent);
+ return intent;
}
/**
@@ -2471,8 +2459,7 @@
if (!ps.isShortcutExistsAndVisibleToPublisher(id)) {
continue;
}
- ShortcutInfo removed = ps.deleteDynamicWithId(id, /*ignoreInvisible=*/ true,
- /*wasPushedOut*/ false);
+ ShortcutInfo removed = ps.deleteDynamicWithId(id, /*ignoreInvisible=*/ true);
if (removed == null) {
if (changedShortcuts == null) {
changedShortcuts = new ArrayList<>(1);
@@ -3096,51 +3083,6 @@
}
@Override
- public void getShortcutsAsync(int launcherUserId,
- @NonNull String callingPackage, long changedSince,
- @Nullable String packageName, @Nullable List<String> shortcutIds,
- @Nullable List<LocusId> locusIds, @Nullable ComponentName componentName,
- int queryFlags, int userId, int callingPid, int callingUid,
- @NonNull AndroidFuture<List<ShortcutInfo>> cb) {
- final List<ShortcutInfo> ret = getShortcuts(launcherUserId, callingPackage,
- changedSince, packageName, shortcutIds, locusIds, componentName, queryFlags,
- userId, callingPid, callingUid);
- if (shortcutIds == null || packageName == null || ret.size() >= shortcutIds.size()) {
- // skip persistence layer if not querying by id in a specific package or all
- // shortcuts have already been found.
- cb.complete(ret);
- return;
- }
- final ShortcutPackage p;
- synchronized (mServiceLock) {
- p = getUserShortcutsLocked(userId).getPackageShortcutsIfExists(packageName);
- }
- if (p == null) {
- cb.complete(ret);
- return; // Bail-out directly if package doesn't exist.
- }
- // fetch remaining shortcuts from persistence layer
- final ArraySet<String> ids = new ArraySet<>(shortcutIds);
- // remove the ids that are already fetched
- ret.stream().map(ShortcutInfo::getId).collect(Collectors.toList()).forEach(ids::remove);
-
- int flags = ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER;
- if ((queryFlags & ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY) != 0) {
- flags = ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO;
- } else if ((queryFlags & ShortcutQuery.FLAG_GET_PERSONS_DATA) != 0) {
- flags &= ~ShortcutInfo.CLONE_REMOVE_PERSON;
- }
- final int cloneFlag = flags;
-
- p.getShortcutByIdsAsync(ids, shortcuts -> {
- if (shortcuts != null) {
- shortcuts.stream().map(si -> si.clone(cloneFlag)).forEach(ret::add);
- }
- cb.complete(ret);
- });
- }
-
- @Override
public boolean isPinnedByCaller(int launcherUserId, @NonNull String callingPackage,
@NonNull String packageName, @NonNull String shortcutId, int userId) {
Preconditions.checkStringNotEmpty(packageName, "packageName");
@@ -3183,27 +3125,6 @@
return list.size() == 0 ? null : list.get(0);
}
- private void getShortcutInfoAsync(
- int launcherUserId, @NonNull String packageName, @NonNull String shortcutId,
- int userId, @NonNull Consumer<ShortcutInfo> cb) {
- Preconditions.checkStringNotEmpty(packageName, "packageName");
- Preconditions.checkStringNotEmpty(shortcutId, "shortcutId");
-
- throwIfUserLockedL(userId);
- throwIfUserLockedL(launcherUserId);
-
- final ShortcutPackage p;
- synchronized (mServiceLock) {
- p = getUserShortcutsLocked(userId).getPackageShortcutsIfExists(packageName);
- }
- if (p == null) {
- cb.accept(null);
- return;
- }
- p.getShortcutByIdsAsync(Collections.singleton(shortcutId), shortcuts ->
- cb.accept(shortcuts == null || shortcuts.isEmpty() ? null : shortcuts.get(0)));
- }
-
@Override
public void pinShortcuts(int launcherUserId,
@NonNull String callingPackage, @NonNull String packageName,
@@ -3388,48 +3309,6 @@
}
@Override
- public void createShortcutIntentsAsync(int launcherUserId,
- @NonNull String callingPackage, @NonNull String packageName,
- @NonNull String shortcutId, int userId, int callingPid,
- int callingUid, @NonNull AndroidFuture<Intent[]> cb) {
- // Calling permission must be checked by LauncherAppsImpl.
- Preconditions.checkStringNotEmpty(packageName, "packageName can't be empty");
- Preconditions.checkStringNotEmpty(shortcutId, "shortcutId can't be empty");
-
- // Check in memory shortcut first
- synchronized (mServiceLock) {
- throwIfUserLockedL(userId);
- throwIfUserLockedL(launcherUserId);
-
- getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
- .attemptToRestoreIfNeededAndSave();
-
- final boolean getPinnedByAnyLauncher =
- canSeeAnyPinnedShortcut(callingPackage, launcherUserId,
- callingPid, callingUid);
-
- // Make sure the shortcut is actually visible to the launcher.
- final ShortcutInfo si = getShortcutInfoLocked(
- launcherUserId, callingPackage, packageName, shortcutId, userId,
- getPinnedByAnyLauncher);
- if (si != null) {
- if (!si.isEnabled() || !(si.isAlive() || getPinnedByAnyLauncher)) {
- Log.e(TAG, "Shortcut " + shortcutId + " does not exist or disabled");
- cb.complete(null);
- return;
- }
- cb.complete(si.getIntents());
- return;
- }
- }
-
- // Otherwise check persisted shortcuts
- getShortcutInfoAsync(launcherUserId, packageName, shortcutId, userId, si -> {
- cb.complete(si == null ? null : si.getIntents());
- });
- }
-
- @Override
public void addListener(@NonNull ShortcutChangeListener listener) {
synchronized (mServiceLock) {
mListeners.add(Objects.requireNonNull(listener));
@@ -3534,41 +3413,6 @@
}
}
- @Override
- public void getShortcutIconFdAsync(int launcherUserId, @NonNull String callingPackage,
- @NonNull String packageName, @NonNull String shortcutId, int userId,
- @NonNull AndroidFuture<ParcelFileDescriptor> cb) {
- Objects.requireNonNull(callingPackage, "callingPackage");
- Objects.requireNonNull(packageName, "packageName");
- Objects.requireNonNull(shortcutId, "shortcutId");
-
- // Checks shortcuts in memory first
- final ShortcutPackage p;
- synchronized (mServiceLock) {
- throwIfUserLockedL(userId);
- throwIfUserLockedL(launcherUserId);
-
- getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
- .attemptToRestoreIfNeededAndSave();
-
- p = getUserShortcutsLocked(userId).getPackageShortcutsIfExists(packageName);
- if (p == null) {
- cb.complete(null);
- return;
- }
-
- final ShortcutInfo shortcutInfo = p.findShortcutById(shortcutId);
- if (shortcutInfo != null) {
- cb.complete(getShortcutIconParcelFileDescriptor(p, shortcutInfo));
- return;
- }
- }
-
- // Otherwise check persisted shortcuts
- getShortcutInfoAsync(launcherUserId, packageName, shortcutId, userId, si ->
- cb.complete(getShortcutIconParcelFileDescriptor(p, si)));
- }
-
@Nullable
private ParcelFileDescriptor getShortcutIconParcelFileDescriptor(
@Nullable final ShortcutPackage p, @Nullable final ShortcutInfo shortcutInfo) {
@@ -3619,44 +3463,6 @@
}
}
- @Override
- public void getShortcutIconUriAsync(int launcherUserId, @NonNull String launcherPackage,
- @NonNull String packageName, @NonNull String shortcutId, int userId,
- @NonNull AndroidFuture<String> cb) {
- Objects.requireNonNull(launcherPackage, "launcherPackage");
- Objects.requireNonNull(packageName, "packageName");
- Objects.requireNonNull(shortcutId, "shortcutId");
-
- // Checks shortcuts in memory first
- synchronized (mServiceLock) {
- throwIfUserLockedL(userId);
- throwIfUserLockedL(launcherUserId);
-
- getLauncherShortcutsLocked(launcherPackage, userId, launcherUserId)
- .attemptToRestoreIfNeededAndSave();
-
- final ShortcutPackage p = getUserShortcutsLocked(userId)
- .getPackageShortcutsIfExists(packageName);
- if (p == null) {
- cb.complete(null);
- return;
- }
-
- final ShortcutInfo shortcutInfo = p.findShortcutById(shortcutId);
- if (shortcutInfo != null) {
- cb.complete(getShortcutIconUriInternal(launcherUserId, launcherPackage,
- packageName, shortcutInfo, userId));
- return;
- }
- }
-
- // Otherwise check persisted shortcuts
- getShortcutInfoAsync(launcherUserId, packageName, shortcutId, userId, si -> {
- cb.complete(si == null ? null : getShortcutIconUriInternal(launcherUserId,
- launcherPackage, packageName, si, userId));
- });
- }
-
private String getShortcutIconUriInternal(int launcherUserId,
@NonNull String launcherPackage, @NonNull String packageName,
@NonNull ShortcutInfo shortcutInfo, int userId) {
@@ -3865,7 +3671,6 @@
synchronized (mServiceLock) {
if (mHandler.hasCallbacks(mSaveDirtyInfoRunner)) {
mHandler.removeCallbacks(mSaveDirtyInfoRunner);
- forEachLoadedUserLocked(ShortcutUser::cancelAllInFlightTasks);
saveDirtyInfo();
}
mShutdown.set(true);
diff --git a/services/core/java/com/android/server/pm/ShortcutUser.java b/services/core/java/com/android/server/pm/ShortcutUser.java
index b35f9c2..bc8cc7b 100644
--- a/services/core/java/com/android/server/pm/ShortcutUser.java
+++ b/services/core/java/com/android/server/pm/ShortcutUser.java
@@ -18,8 +18,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
-import android.app.appsearch.AppSearchManager;
-import android.app.appsearch.AppSearchSession;
import android.content.pm.ShortcutManager;
import android.content.pm.UserPackage;
import android.metrics.LogMaker;
@@ -34,7 +32,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.infra.AndroidFuture;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.modules.utils.TypedXmlPullParser;
@@ -83,7 +80,6 @@
private static final String KEY_PACKAGES = "packages";
final ShortcutService mService;
- final AppSearchManager mAppSearchManager;
final Executor mExecutor;
@UserIdInt
@@ -105,14 +101,9 @@
private final Object mLock = new Object();
- @GuardedBy("mLock")
- private final ArrayList<AndroidFuture<AppSearchSession>> mInFlightSessions = new ArrayList<>();
-
public ShortcutUser(ShortcutService service, int userId) {
mService = service;
mUserId = userId;
- mAppSearchManager = service.mContext.createContextAsUser(UserHandle.of(userId), 0)
- .getSystemService(AppSearchManager.class);
mExecutor = FgThread.getExecutor();
}
@@ -154,12 +145,7 @@
public ShortcutPackage removePackage(@NonNull String packageName) {
final ShortcutPackage removed = mPackages.remove(packageName);
-
- if (removed != null) {
- removed.removeAllShortcutsAsync();
- }
mService.cleanupBitmapsForPackage(mUserId, packageName);
-
return removed;
}
@@ -524,7 +510,6 @@
Log.w(TAG, "Shortcuts for package " + sp.getPackageName() + " are being restored."
+ " Existing non-manifeset shortcuts will be overwritten.");
}
- sp.removeAllShortcutsAsync();
addPackage(sp);
restoredPackages[0]++;
restoredShortcuts[0] += sp.getShortcutCount();
@@ -660,47 +645,4 @@
logger.write(logMaker.setType(MetricsEvent.SHORTCUTS_CHANGED_SHORTCUT_COUNT)
.setSubtype(totalSharingShortcutCount));
}
-
- @NonNull
- AndroidFuture<AppSearchSession> getAppSearch(
- @NonNull final AppSearchManager.SearchContext searchContext) {
- final AndroidFuture<AppSearchSession> future = new AndroidFuture<>();
- synchronized (mLock) {
- mInFlightSessions.removeIf(CompletableFuture::isDone);
- mInFlightSessions.add(future);
- }
- if (mAppSearchManager == null) {
- future.completeExceptionally(new RuntimeException("app search manager is null"));
- return future;
- }
- if (!mService.mUserManagerInternal.isUserUnlockingOrUnlocked(getUserId())) {
- // In rare cases the user might be stopped immediate after it started, in these cases
- // any on-going session will need to be abandoned.
- future.completeExceptionally(new RuntimeException("User " + getUserId() + " is "));
- return future;
- }
- final long callingIdentity = Binder.clearCallingIdentity();
- try {
- mAppSearchManager.createSearchSession(searchContext, mExecutor, result -> {
- if (!result.isSuccess()) {
- future.completeExceptionally(
- new RuntimeException(result.getErrorMessage()));
- return;
- }
- future.complete(result.getResultValue());
- });
- } finally {
- Binder.restoreCallingIdentity(callingIdentity);
- }
- return future;
- }
-
- void cancelAllInFlightTasks() {
- synchronized (mLock) {
- for (AndroidFuture<AppSearchSession> session : mInFlightSessions) {
- session.cancel(true);
- }
- mInFlightSessions.clear();
- }
- }
}
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 a0bc77e..c4e4c42 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.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.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.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 @@
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 @@
@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 @@
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 @@
}
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 @@
}
}
- 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 @@
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 @@
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 @@
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 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 @@
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 @@
}
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 @@
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/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index b71256d..e4ad56f 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -225,6 +225,7 @@
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
import static com.android.server.wm.ActivityTaskManagerService.getInputDispatchingTimeoutMillisLocked;
+import static com.android.server.wm.ActivityTaskManagerService.isPip2ExperimentEnabled;
import static com.android.server.wm.IdentifierProto.HASH_CODE;
import static com.android.server.wm.IdentifierProto.TITLE;
import static com.android.server.wm.IdentifierProto.USER_ID;
@@ -1495,7 +1496,10 @@
// precede the configuration change from the resize.)
mLastReportedPictureInPictureMode = inPictureInPictureMode;
mLastReportedMultiWindowMode = inPictureInPictureMode;
- ensureActivityConfiguration(true /* ignoreVisibility */);
+ if (!isPip2ExperimentEnabled()) {
+ // PiP2 should handle sending out the configuration as a part of Shell Transitions.
+ ensureActivityConfiguration(true /* ignoreVisibility */);
+ }
if (inPictureInPictureMode && findMainWindow() == null
&& task.topRunningActivity() == this) {
// Prevent malicious app entering PiP without valid WindowState, which can in turn
diff --git a/services/core/java/com/android/server/wm/ActivitySnapshotController.java b/services/core/java/com/android/server/wm/ActivitySnapshotController.java
index cfd3248..26b7cc6 100644
--- a/services/core/java/com/android/server/wm/ActivitySnapshotController.java
+++ b/services/core/java/com/android/server/wm/ActivitySnapshotController.java
@@ -34,6 +34,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.wm.BaseAppSnapshotPersister.PersistInfoProvider;
+import com.android.window.flags.Flags;
import java.io.File;
import java.io.PrintWriter;
@@ -498,50 +499,73 @@
}
final TaskFragment currTF = currentActivity.getTaskFragment();
final TaskFragment prevTF = initPrev.getTaskFragment();
- final TaskFragment prevAdjacentTF = prevTF != null
- ? prevTF.getAdjacentTaskFragment() : null;
- if (currTF == prevTF && currTF != null || prevAdjacentTF == null) {
- // Current activity and previous one is in the same task fragment, or
- // previous activity is not in a task fragment, or
- // previous activity's task fragment doesn't adjacent to any others.
+ if (currTF == prevTF || prevTF.asTask() != null || !prevTF.hasAdjacentTaskFragment()) {
+ // Current activity and the initPrev is in the same TaskFragment,
+ // or initPrev activity is a direct child of Task,
+ // or initPrev activity doesn't have an adjacent.
+ // A
+ // B
if (!inTransition || isInParticipant(initPrev, mTmpTransitionParticipants)) {
result.add(initPrev);
}
return;
}
- if (prevAdjacentTF == currTF) {
+ if (currTF.isAdjacentTo(prevTF)) {
// previous activity A is adjacent to current activity B.
// Try to find anyone below previous activityA, which are C and D if exists.
// A | B
// C (| D)
getActivityBelow(initPrev, inTransition, result);
- } else {
- // previous activity C isn't adjacent to current activity A.
- // A
- // B | C
- final Task prevAdjacentTask = prevAdjacentTF.getTask();
- if (prevAdjacentTask == currentTask) {
- final int currentIndex = currTF != null
- ? currentTask.mChildren.indexOf(currTF)
- : currentTask.mChildren.indexOf(currentActivity);
- final int prevAdjacentIndex =
- prevAdjacentTask.mChildren.indexOf(prevAdjacentTF);
- // prevAdjacentTF already above currentActivity
- if (prevAdjacentIndex > currentIndex) {
- return;
- }
+ return;
+ }
+
+ // The initPrev activity has an adjacent that is different from current activity.
+ // A
+ // B | C
+ final int currentIndex = currTF.asTask() != null
+ ? currentTask.mChildren.indexOf(currentActivity)
+ : currentTask.mChildren.indexOf(currTF);
+ if (!Flags.allowMultipleAdjacentTaskFragments()) {
+ final int prevAdjacentIndex = currentTask.mChildren.indexOf(
+ prevTF.getAdjacentTaskFragment());
+ if (prevAdjacentIndex > currentIndex) {
+ // PrevAdjacentTF already above currentActivity
+ return;
}
+ // Add both the one below, and its adjacent.
if (!inTransition || isInParticipant(initPrev, mTmpTransitionParticipants)) {
result.add(initPrev);
}
- // prevAdjacentTF is adjacent to another one
+ final ActivityRecord prevAdjacentActivity = prevTF.getAdjacentTaskFragment()
+ .getTopMostActivity();
+ if (prevAdjacentActivity != null && (!inTransition
+ || isInParticipant(prevAdjacentActivity, mTmpTransitionParticipants))) {
+ result.add(prevAdjacentActivity);
+ }
+ return;
+ }
+
+ final boolean hasAdjacentAboveCurrent = prevTF.forOtherAdjacentTaskFragments(
+ prevAdjacentTF -> {
+ final int prevAdjacentIndex = currentTask.mChildren.indexOf(prevAdjacentTF);
+ return prevAdjacentIndex > currentIndex;
+ });
+ if (hasAdjacentAboveCurrent) {
+ // PrevAdjacentTF already above currentActivity
+ return;
+ }
+ // Add all adjacent top.
+ if (!inTransition || isInParticipant(initPrev, mTmpTransitionParticipants)) {
+ result.add(initPrev);
+ }
+ prevTF.forOtherAdjacentTaskFragments(prevAdjacentTF -> {
final ActivityRecord prevAdjacentActivity = prevAdjacentTF.getTopMostActivity();
if (prevAdjacentActivity != null && (!inTransition
|| isInParticipant(prevAdjacentActivity, mTmpTransitionParticipants))) {
result.add(prevAdjacentActivity);
}
- }
+ });
}
static boolean isInParticipant(ActivityRecord ar,
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 0aff1de..ef6f923 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -2526,9 +2526,6 @@
task.forAllActivities(r -> {
if (!r.attachedToProcess()) return;
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.
mMultiWindowModeChangedActivities.remove(r);
});
diff --git a/services/core/java/com/android/server/wm/BLASTSyncEngine.java b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
index 7deb6a8..dbe0faf 100644
--- a/services/core/java/com/android/server/wm/BLASTSyncEngine.java
+++ b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
@@ -270,6 +270,11 @@
() -> callback.onCommitted(new SurfaceControl.Transaction()));
mHandler.postDelayed(callback, BLAST_TIMEOUT_DURATION);
+ if (mWm.mAnimator.mPendingState == WindowAnimator.PENDING_STATE_NEED_APPLY) {
+ // Applies pending transaction before onTransactionReady to ensure the order with
+ // sync transaction. This is unlikely to happen unless animator thread is slow.
+ mWm.mAnimator.applyPendingTransaction();
+ }
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "onTransactionReady");
mListener.onTransactionReady(mSyncId, merged);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
@@ -353,6 +358,10 @@
+ " for non-sync " + wc);
wc.mSyncGroup = null;
}
+ if (mWm.mAnimator.mPendingState == WindowAnimator.PENDING_STATE_HAS_CHANGES
+ && wc.mSyncState != WindowContainer.SYNC_STATE_NONE) {
+ mWm.mAnimator.mPendingState = WindowAnimator.PENDING_STATE_NEED_APPLY;
+ }
if (mReady) {
mWm.mWindowPlacerLocked.requestTraversal();
}
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index fc0df64..819395a 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -453,7 +453,7 @@
outPrevActivities.add(prevActivity);
return true;
}
- if (currTF.getAdjacentTaskFragment() == null) {
+ if (!currTF.hasAdjacentTaskFragment()) {
final TaskFragment nextTF = findNextTaskFragment(currentTask, currTF);
if (isSecondCompanionToFirst(currTF, nextTF)) {
// TF is isStacked, search bottom activity from companion TF.
@@ -476,7 +476,21 @@
}
} else {
// If adjacent TF has companion to current TF, those two TF will be closed together.
- final TaskFragment adjacentTF = currTF.getAdjacentTaskFragment();
+ final TaskFragment adjacentTF;
+ if (Flags.allowMultipleAdjacentTaskFragments()) {
+ if (currTF.getAdjacentTaskFragments().size() > 2) {
+ throw new IllegalStateException(
+ "Not yet support 3+ adjacent for non-Task TFs");
+ }
+ final TaskFragment[] tmpAdjacent = new TaskFragment[1];
+ currTF.forOtherAdjacentTaskFragments(tf -> {
+ tmpAdjacent[0] = tf;
+ return true;
+ });
+ adjacentTF = tmpAdjacent[0];
+ } else {
+ adjacentTF = currTF.getAdjacentTaskFragment();
+ }
if (isSecondCompanionToFirst(currTF, adjacentTF)) {
// The two TFs are adjacent (visually displayed side-by-side), search if any
// activity below the lowest one.
@@ -533,29 +547,47 @@
return;
}
- final TaskFragment prevTFAdjacent = prevTF.getAdjacentTaskFragment();
- if (prevTFAdjacent == null || prevTFAdjacent.asTask() != null) {
+ if (!prevTF.hasAdjacentTaskFragment()) {
return;
}
- final ActivityRecord prevActivityAdjacent =
- prevTFAdjacent.getTopNonFinishingActivity();
- if (prevActivityAdjacent != null) {
- outPrevActivities.add(prevActivityAdjacent);
+ if (!Flags.allowMultipleAdjacentTaskFragments()) {
+ final TaskFragment prevTFAdjacent = prevTF.getAdjacentTaskFragment();
+ final ActivityRecord prevActivityAdjacent =
+ prevTFAdjacent.getTopNonFinishingActivity();
+ if (prevActivityAdjacent != null) {
+ outPrevActivities.add(prevActivityAdjacent);
+ }
+ return;
}
+ prevTF.forOtherAdjacentTaskFragments(prevTFAdjacent -> {
+ final ActivityRecord prevActivityAdjacent =
+ prevTFAdjacent.getTopNonFinishingActivity();
+ if (prevActivityAdjacent != null) {
+ outPrevActivities.add(prevActivityAdjacent);
+ }
+ });
}
private static void findAdjacentActivityIfExist(@NonNull ActivityRecord mainActivity,
@NonNull ArrayList<ActivityRecord> outList) {
final TaskFragment mainTF = mainActivity.getTaskFragment();
- if (mainTF == null || mainTF.getAdjacentTaskFragment() == null) {
+ if (mainTF == null || !mainTF.hasAdjacentTaskFragment()) {
return;
}
- final TaskFragment adjacentTF = mainTF.getAdjacentTaskFragment();
- final ActivityRecord topActivity = adjacentTF.getTopNonFinishingActivity();
- if (topActivity == null) {
+ if (!Flags.allowMultipleAdjacentTaskFragments()) {
+ final TaskFragment adjacentTF = mainTF.getAdjacentTaskFragment();
+ final ActivityRecord topActivity = adjacentTF.getTopNonFinishingActivity();
+ if (topActivity != null) {
+ outList.add(topActivity);
+ }
return;
}
- outList.add(topActivity);
+ mainTF.forOtherAdjacentTaskFragments(adjacentTF -> {
+ final ActivityRecord topActivity = adjacentTF.getTopNonFinishingActivity();
+ if (topActivity != null) {
+ outList.add(topActivity);
+ }
+ });
}
private static boolean hasTranslucentActivity(@NonNull ActivityRecord currentActivity,
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 5c3fbdf..09214cd 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -100,6 +100,7 @@
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_WALLPAPER;
import static com.android.internal.protolog.WmProtoLogGroups.WM_SHOW_TRANSACTIONS;
import static com.android.internal.util.LatencyTracker.ACTION_ROTATE_SCREEN;
+import static com.android.server.display.feature.flags.Flags.enableDisplayContentModeManagement;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
@@ -3288,6 +3289,32 @@
return new Point(w, h);
}
+ void onDisplayInfoChangeApplied() {
+ if (!enableDisplayContentModeManagement()) {
+ Slog.e(TAG, "ShouldShowSystemDecors shouldn't be updated when the flag is off.");
+ }
+
+ final boolean shouldShow;
+ if (isDefaultDisplay) {
+ shouldShow = true;
+ } else if (isPrivate()) {
+ shouldShow = false;
+ } else {
+ shouldShow = mDisplay.canHostTasks();
+ }
+
+ if (shouldShow == mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(this)) {
+ return;
+ }
+ mWmService.mDisplayWindowSettings.setShouldShowSystemDecorsLocked(this, shouldShow);
+
+ if (shouldShow) {
+ mRootWindowContainer.startSystemDecorations(this, "onDisplayInfoChangeApplied");
+ } else {
+ clearAllTasksOnDisplay(null);
+ }
+ }
+
DisplayCutout loadDisplayCutout(int displayWidth, int displayHeight) {
if (mDisplayPolicy == null || mInitialDisplayCutout == null) {
return null;
@@ -6522,10 +6549,8 @@
return mRemoving;
}
- void remove() {
- mRemoving = true;
+ private void clearAllTasksOnDisplay(@Nullable Runnable clearTasksCallback) {
Task lastReparentedRootTask;
-
mRootWindowContainer.mTaskSupervisor.beginDeferResume();
try {
lastReparentedRootTask = reduceOnAllTaskDisplayAreas((taskDisplayArea, rootTask) -> {
@@ -6538,10 +6563,9 @@
} finally {
mRootWindowContainer.mTaskSupervisor.endDeferResume();
}
- mRemoved = true;
- if (mContentRecorder != null) {
- mContentRecorder.stopRecording();
+ if (clearTasksCallback != null) {
+ clearTasksCallback.run();
}
// Only update focus/visibility for the last one because there may be many root tasks are
@@ -6549,6 +6573,19 @@
if (lastReparentedRootTask != null) {
lastReparentedRootTask.resumeNextFocusAfterReparent();
}
+ }
+
+ void remove() {
+ mRemoving = true;
+
+ clearAllTasksOnDisplay(() -> {
+ mRemoved = true;
+
+ if (mContentRecorder != null) {
+ mContentRecorder.stopRecording();
+ }
+ });
+
releaseSelfIfNeeded();
mDisplayPolicy.release();
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 4f36476..57fe0bb 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -46,6 +46,7 @@
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_TASKS;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_WALLPAPER;
import static com.android.internal.protolog.WmProtoLogGroups.WM_SHOW_SURFACE_ALLOC;
+import static com.android.server.display.feature.flags.Flags.enableDisplayContentModeManagement;
import static com.android.server.policy.PhoneWindowManager.SYSTEM_DIALOG_REASON_ASSIST;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
@@ -151,6 +152,7 @@
import com.android.server.am.ActivityManagerService;
import com.android.server.am.AppTimeTracker;
import com.android.server.am.UserState;
+import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.pm.UserManagerInternal;
import com.android.server.policy.PermissionPolicyInternal;
import com.android.server.policy.WindowManagerPolicy;
@@ -1438,6 +1440,13 @@
: getDefaultTaskDisplayArea();
}
+ // When display content mode management flag is enabled, the task display area is marked as
+ // removed when switching from extended display to mirroring display. We need to restart the
+ // task display area before starting the home.
+ if (enableDisplayContentModeManagement() && taskDisplayArea.isRemoved()) {
+ taskDisplayArea.restart();
+ }
+
Intent homeIntent = null;
ActivityInfo aInfo = null;
if (taskDisplayArea == getDefaultTaskDisplayArea()
@@ -2856,20 +2865,24 @@
if (display == null) {
return;
}
- // Do not start home before booting, or it may accidentally finish booting before it
- // starts. Instead, we expect home activities to be launched when the system is ready
- // (ActivityManagerService#systemReady).
- if (mService.isBooted() || mService.isBooting()) {
- startSystemDecorations(display);
- }
+
+ startSystemDecorations(display, "displayAdded");
+
// Drop any cached DisplayInfos associated with this display id - the values are now
// out of date given this display added event.
mWmService.mPossibleDisplayInfoMapper.removePossibleDisplayInfos(displayId);
}
}
- private void startSystemDecorations(final DisplayContent displayContent) {
- startHomeOnDisplay(mCurrentUser, "displayAdded", displayContent.getDisplayId());
+ void startSystemDecorations(final DisplayContent displayContent, String reason) {
+ // Do not start home before booting, or it may accidentally finish booting before it
+ // starts. Instead, we expect home activities to be launched when the system is ready
+ // (ActivityManagerService#systemReady).
+ if (!mService.isBooted() && !mService.isBooting()) {
+ return;
+ }
+
+ startHomeOnDisplay(mCurrentUser, reason, displayContent.getDisplayId());
displayContent.getDisplayPolicy().notifyDisplayReady();
}
@@ -2896,7 +2909,13 @@
synchronized (mService.mGlobalLock) {
final DisplayContent displayContent = getDisplayContent(displayId);
if (displayContent != null) {
- displayContent.requestDisplayUpdate(() -> clearDisplayInfoCaches(displayId));
+ displayContent.requestDisplayUpdate(
+ () -> {
+ clearDisplayInfoCaches(displayId);
+ if (enableDisplayContentModeManagement()) {
+ displayContent.onDisplayInfoChangeApplied();
+ }
+ });
} else {
clearDisplayInfoCaches(displayId);
}
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index 9a48d5b..d7b6d96 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -200,6 +200,7 @@
}
mSnapshot.startAnimation(t, snapshotAnim, type);
}
+ setAnimatorPendingState(t);
}
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
@@ -208,6 +209,14 @@
null /* animationCancelledCallback */, null /* snapshotAnim */, null /* freezer */);
}
+ /** Indicates that there are surface operations in the pending transaction. */
+ private void setAnimatorPendingState(Transaction t) {
+ if (mService.mAnimator.mPendingState == WindowAnimator.PENDING_STATE_NONE
+ && t == mAnimatable.getPendingTransaction()) {
+ mService.mAnimator.mPendingState = WindowAnimator.PENDING_STATE_HAS_CHANGES;
+ }
+ }
+
/** Returns whether it is currently running an animation. */
boolean isAnimating() {
return mAnimation != null;
@@ -357,6 +366,7 @@
final boolean scheduleAnim = removeLeash(t, mAnimatable, leash, destroyLeash);
mAnimationFinished = false;
if (scheduleAnim) {
+ setAnimatorPendingState(t);
mService.scheduleAnimationLocked();
}
}
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 3d0b41b..3634bc9 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -1887,6 +1887,11 @@
return lastReparentedRootTask;
}
+ // TODO(b/385263090): Remove this method
+ void restart() {
+ mRemoved = false;
+ }
+
/**
* Returns the {@link TaskDisplayArea} to which root tasks should be reparented.
*
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index cb6b690..7b7b3eb 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -3523,10 +3523,18 @@
throw new IllegalStateException("allowMultipleAdjacentTaskFragments must be"
+ " enabled to set more than two TaskFragments adjacent to each other.");
}
- if (taskFragments.size() < 2) {
+ final int size = taskFragments.size();
+ if (size < 2) {
throw new IllegalArgumentException("Adjacent TaskFragments must contain at least"
- + " two TaskFragments, but only " + taskFragments.size()
- + " were provided.");
+ + " two TaskFragments, but only " + size + " were provided.");
+ }
+ if (size > 2) {
+ for (int i = 0; i < size; i++) {
+ if (taskFragments.valueAt(i).asTask() == null) {
+ throw new IllegalArgumentException(
+ "Not yet support 3+ adjacent for non-Task TFs");
+ }
+ }
}
mAdjacentSet = taskFragments;
}
@@ -3604,6 +3612,10 @@
return false;
}
+ int size() {
+ return mAdjacentSet.size();
+ }
+
@Override
public boolean equals(@Nullable Object o) {
if (this == o) {
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 49c8559..790ae1e 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -26,6 +26,7 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import android.annotation.IntDef;
import android.content.Context;
import android.os.HandlerExecutor;
import android.os.Trace;
@@ -38,6 +39,8 @@
import com.android.server.policy.WindowManagerPolicy;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
/**
@@ -86,6 +89,25 @@
private final SurfaceControl.Transaction mTransaction;
+ /** The pending transaction is applied. */
+ static final int PENDING_STATE_NONE = 0;
+ /** There are some (significant) operations set to the pending transaction. */
+ static final int PENDING_STATE_HAS_CHANGES = 1;
+ /** The pending transaction needs to be applied before sending sync transaction to shell. */
+ static final int PENDING_STATE_NEED_APPLY = 2;
+
+ @IntDef(prefix = { "PENDING_STATE_" }, value = {
+ PENDING_STATE_NONE,
+ PENDING_STATE_HAS_CHANGES,
+ PENDING_STATE_NEED_APPLY,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface PendingState {}
+
+ /** The global state of pending transaction. */
+ @PendingState
+ int mPendingState;
+
WindowAnimator(final WindowManagerService service) {
mService = service;
mContext = service.mContext;
@@ -217,6 +239,7 @@
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "applyTransaction");
mTransaction.apply();
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+ mPendingState = PENDING_STATE_NONE;
mService.mWindowTracing.logState("WindowAnimator");
ProtoLog.i(WM_SHOW_TRANSACTIONS, "<<< CLOSE TRANSACTION animate");
@@ -296,8 +319,19 @@
return mAnimationFrameCallbackScheduled;
}
- Choreographer getChoreographer() {
- return mChoreographer;
+ void applyPendingTransaction() {
+ Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "applyPendingTransaction");
+ mPendingState = PENDING_STATE_NONE;
+ final int numDisplays = mService.mRoot.getChildCount();
+ if (numDisplays == 1) {
+ mService.mRoot.getChildAt(0).getPendingTransaction().apply();
+ } else {
+ for (int i = 0; i < numDisplays; i++) {
+ mTransaction.merge(mService.mRoot.getChildAt(i).getPendingTransaction());
+ }
+ mTransaction.apply();
+ }
+ Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
}
/**
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 793f189..965b224 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -9150,7 +9150,7 @@
// handling the touch-outside event to prevent focus rapid changes back-n-forth.
final boolean shouldDelayTouchForEmbeddedActivity = activity != null
&& activity.isEmbedded()
- && activity.getTaskFragment().getAdjacentTaskFragment() != null;
+ && activity.getTaskFragment().hasAdjacentTaskFragment();
// For cases when there are multiple freeform windows where non-top windows are blocking
// the gesture zones, delay handling the touch-outside event to prevent refocusing the
@@ -9529,21 +9529,41 @@
return focusedActivity;
}
- final TaskFragment adjacentTaskFragment = taskFragment.getAdjacentTaskFragment();
- final ActivityRecord adjacentTopActivity =
- adjacentTaskFragment != null ? adjacentTaskFragment.topRunningActivity() : null;
- if (adjacentTopActivity == null) {
- // Return if no adjacent activity.
+ if (!taskFragment.hasAdjacentTaskFragment()) {
return focusedActivity;
}
- if (adjacentTopActivity.getLastWindowCreateTime()
- < focusedActivity.getLastWindowCreateTime()) {
- // Return if the current focus activity has more recently active window.
- return focusedActivity;
+ if (!Flags.allowMultipleAdjacentTaskFragments()) {
+ final TaskFragment adjacentTaskFragment = taskFragment.getAdjacentTaskFragment();
+ final ActivityRecord adjacentTopActivity = adjacentTaskFragment.topRunningActivity();
+ if (adjacentTopActivity == null) {
+ // Return if no adjacent activity.
+ return focusedActivity;
+ }
+
+ if (adjacentTopActivity.getLastWindowCreateTime()
+ < focusedActivity.getLastWindowCreateTime()) {
+ // Return if the current focus activity has more recently active window.
+ return focusedActivity;
+ }
+
+ return adjacentTopActivity;
}
- return adjacentTopActivity;
+ // Find the adjacent activity with more recently active window.
+ final ActivityRecord[] mostRecentActiveActivity = { focusedActivity };
+ final long[] mostRecentActiveTime = { focusedActivity.getLastWindowCreateTime() };
+ taskFragment.forOtherAdjacentTaskFragments(adjacentTaskFragment -> {
+ final ActivityRecord adjacentTopActivity = adjacentTaskFragment.topRunningActivity();
+ if (adjacentTopActivity != null) {
+ final long lastWindowCreateTime = adjacentTopActivity.getLastWindowCreateTime();
+ if (lastWindowCreateTime > mostRecentActiveTime[0]) {
+ mostRecentActiveTime[0] = lastWindowCreateTime;
+ mostRecentActiveActivity[0] = adjacentTopActivity;
+ }
+ }
+ });
+ return mostRecentActiveActivity[0];
}
@NonNull
@@ -9592,14 +9612,28 @@
return false;
}
final TaskFragment fromFragment = fromWin.getTaskFragment();
- if (fromFragment == null) {
- return false;
- }
- final TaskFragment adjacentFragment = fromFragment.getAdjacentTaskFragment();
- if (adjacentFragment == null || adjacentFragment.asTask() != null) {
+ if (fromFragment == null || fromFragment.asTask() != null) {
// Don't move the focus to another task.
return false;
}
+ if (!fromFragment.hasAdjacentTaskFragment()) {
+ // No adjacent window.
+ return false;
+ }
+ final TaskFragment adjacentFragment;
+ if (Flags.allowMultipleAdjacentTaskFragments()) {
+ if (fromFragment.getAdjacentTaskFragments().size() > 2) {
+ throw new IllegalStateException("Not yet support 3+ adjacent for non-Task TFs");
+ }
+ final TaskFragment[] tmpAdjacent = new TaskFragment[1];
+ fromFragment.forOtherAdjacentTaskFragments(adjacentTF -> {
+ tmpAdjacent[0] = adjacentTF;
+ return true;
+ });
+ adjacentFragment = tmpAdjacent[0];
+ } else {
+ adjacentFragment = fromFragment.getAdjacentTaskFragment();
+ }
if (adjacentFragment.isIsolatedNav()) {
// Don't move the focus if the adjacent TF is isolated navigation.
return false;
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index fb197c5..e45ada9 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.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 @@
}
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/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java b/services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
index fc3ec7b..4d04c8b 100644
--- a/services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
+++ b/services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
@@ -37,6 +37,7 @@
import android.util.Log;
import com.android.server.backup.BackupAgentTimeoutParameters;
+import com.android.server.backup.BackupManagerConstants;
import com.android.server.backup.BackupManagerService;
import com.android.server.backup.TransportManager;
import com.android.server.backup.UserBackupManagerService;
@@ -162,10 +163,10 @@
public static UserBackupManagerService.BackupWakeLock createBackupWakeLock(
Application application) {
- PowerManager powerManager =
- (PowerManager) application.getSystemService(Context.POWER_SERVICE);
+ PowerManager powerManager = application.getSystemService(PowerManager.class);
return new UserBackupManagerService.BackupWakeLock(
- powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*"), 0);
+ powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*"), 0,
+ new BackupManagerConstants(Handler.getMain(), application.getContentResolver()));
}
/**
diff --git a/services/tests/appfunctions/Android.bp b/services/tests/appfunctions/Android.bp
index 836f90b..e48abc2 100644
--- a/services/tests/appfunctions/Android.bp
+++ b/services/tests/appfunctions/Android.bp
@@ -33,18 +33,20 @@
],
static_libs: [
- "androidx.test.core",
- "androidx.test.runner",
- "androidx.test.ext.truth",
"androidx.core_core-ktx",
+ "androidx.test.core",
+ "androidx.test.ext.truth",
+ "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 0000000..896d2a21d
--- /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/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java
index 76f7e80..0972ea91 100644
--- a/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java
@@ -33,7 +33,6 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
-import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import android.util.LongSparseArray;
@@ -72,6 +71,7 @@
private static final String TEST_PKG2 = "com.example2";
private static final String TEST_PKG3 = "com.example3";
+ private static final int TEST_USER_ID = 0;
private static final int TEST_UID1 = 10001;
private static final int TEST_UID2 = 10002;
private static final int TEST_UID3 = 10003;
@@ -98,7 +98,7 @@
mService = new BlobStoreManagerService(mContext, new TestInjector());
mUserSessions = new LongSparseArray<>();
- mService.addUserSessionsForTest(mUserSessions, UserHandle.myUserId());
+ mService.addUserSessionsForTest(mUserSessions, TEST_USER_ID);
}
@After
@@ -360,6 +360,7 @@
return createBlobStoreSessionMock(ownerPackageName, ownerUid, sessionId, sessionFile,
mock(BlobHandle.class));
}
+
private BlobStoreSession createBlobStoreSessionMock(String ownerPackageName, int ownerUid,
long sessionId, File sessionFile, BlobHandle blobHandle) {
final BlobStoreSession session = mock(BlobStoreSession.class);
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 de6f9bd..bd15bd0 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
AppHintSession a = (AppHintSession) service.getBinderServiceInstance()
.createHintSessionWithConfig(token, SessionTag.OTHER,
- creationConfig, new SessionConfig());
+ creationConfig, new SessionConfig()).session;
a.updateTargetWorkDuration(100L);
@@ -717,7 +711,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index 0b2a2cd..4e030d4 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -2045,18 +2045,6 @@
return p == null ? null : p.getAllShareTargetsForTest();
}
- protected void resetPersistedShortcuts() {
- final ShortcutPackage p = mService.getPackageShortcutForTest(
- getCallingPackage(), getCallingUserId());
- p.removeAllShortcutsAsync();
- }
-
- protected void getPersistedShortcut(AndroidFuture<List<ShortcutInfo>> cb) {
- final ShortcutPackage p = mService.getPackageShortcutForTest(
- getCallingPackage(), getCallingUserId());
- p.getTopShortcutsFromPersistence(cb);
- }
-
/**
* @return the number of shortcuts stored internally for the caller that can be used as a share
* target in the ShareSheet. Such shortcuts have a matching category with at least one of the
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java
deleted file mode 100644
index 10e8bf0..0000000
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java
+++ /dev/null
@@ -1,297 +0,0 @@
-/*
- * Copyright (C) 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 com.android.server.pm;
-
-import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.list;
-
-import android.app.PendingIntent;
-import android.content.pm.ShortcutInfo;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.os.UserHandle;
-
-import com.android.internal.infra.AndroidFuture;
-
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.TimeUnit;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-
-/**
- * Tests for {@link android.app.appsearch.AppSearchManager} and relevant APIs in ShortcutManager.
- *
- atest -c com.android.server.pm.ShortcutManagerTest12
- */
-public class ShortcutManagerTest12 extends BaseShortcutManagerTest {
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mService.updateConfigurationLocked(
- ShortcutService.ConfigConstants.KEY_MAX_SHORTCUTS + "=5,"
- + ShortcutService.ConfigConstants.KEY_SAVE_DELAY_MILLIS + "=1");
- }
-
- @Override
- protected void tearDown() throws Exception {
- if (mService.isAppSearchEnabled()) {
- setCaller(CALLING_PACKAGE_1, USER_10);
- mService.getPackageShortcutForTest(CALLING_PACKAGE_1, USER_10)
- .removeAllShortcutsAsync();
- }
- super.tearDown();
- }
-
- public void testGetShortcutIntents_ReturnsMutablePendingIntents() throws RemoteException {
- setDefaultLauncher(USER_10, LAUNCHER_1);
-
- runWithCaller(CALLING_PACKAGE_1, USER_10, () ->
- assertTrue(mManager.setDynamicShortcuts(list(makeShortcut("s1"))))
- );
-
- runWithCaller(LAUNCHER_1, USER_10, () -> {
- final PendingIntent intent = mLauncherApps.getShortcutIntent(
- CALLING_PACKAGE_1, "s1", null, UserHandle.SYSTEM);
- assertNotNull(intent);
- });
- }
-
- public void testSetDynamicShortcuts_PersistsShortcutsToDisk() throws RemoteException {
- if (!mService.isAppSearchEnabled()) {
- return;
- }
- setCaller(CALLING_PACKAGE_1, USER_10);
- // Verifies setDynamicShortcuts persists shortcuts into AppSearch
- mManager.setDynamicShortcuts(list(
- makeShortcut("s1"),
- makeShortcut("s2"),
- makeShortcut("s3")
- ));
- List<ShortcutInfo> shortcuts = getAllPersistedShortcuts();
- assertNotNull(shortcuts);
- assertEquals(3, shortcuts.size());
- Set<String> shortcutIds =
- shortcuts.stream().map(ShortcutInfo::getId).collect(Collectors.toSet());
- assertTrue(shortcutIds.contains("s1"));
- assertTrue(shortcutIds.contains("s2"));
- assertTrue(shortcutIds.contains("s3"));
-
- // Verifies removeAllDynamicShortcuts removes shortcuts from persistence layer
- mManager.removeAllDynamicShortcuts();
- shortcuts = getAllPersistedShortcuts();
- assertNotNull(shortcuts);
- assertTrue(shortcuts.isEmpty());
- }
-
- public void testAddDynamicShortcuts_PersistsShortcutsToDisk() {
- if (!mService.isAppSearchEnabled()) {
- return;
- }
- setCaller(CALLING_PACKAGE_1, USER_10);
- mManager.setDynamicShortcuts(list(
- makeShortcut("s1"),
- makeShortcut("s2"),
- makeShortcut("s3")
- ));
- // Verifies addDynamicShortcuts persists shortcuts into AppSearch
- mManager.addDynamicShortcuts(list(makeShortcut("s4"), makeShortcut("s5")));
- final List<ShortcutInfo> shortcuts = getAllPersistedShortcuts();
- assertNotNull(shortcuts);
- assertEquals(5, shortcuts.size());
- final Set<String> shortcutIds =
- shortcuts.stream().map(ShortcutInfo::getId).collect(Collectors.toSet());
- assertTrue(shortcutIds.contains("s1"));
- assertTrue(shortcutIds.contains("s2"));
- assertTrue(shortcutIds.contains("s3"));
- assertTrue(shortcutIds.contains("s4"));
- assertTrue(shortcutIds.contains("s5"));
- }
-
- public void testPushDynamicShortcuts_PersistsShortcutsToDisk() {
- if (!mService.isAppSearchEnabled()) {
- return;
- }
- setCaller(CALLING_PACKAGE_1, USER_10);
- mManager.setDynamicShortcuts(list(
- makeShortcut("s1"),
- makeShortcut("s2"),
- makeShortcut("s3"),
- makeShortcut("s4"),
- makeShortcut("s5")
- ));
- List<ShortcutInfo> shortcuts = getAllPersistedShortcuts();
- assertNotNull(shortcuts);
- assertEquals(5, shortcuts.size());
- Set<String> shortcutIds =
- shortcuts.stream().map(ShortcutInfo::getId).collect(Collectors.toSet());
- assertTrue(shortcutIds.contains("s1"));
- assertTrue(shortcutIds.contains("s2"));
- assertTrue(shortcutIds.contains("s3"));
- assertTrue(shortcutIds.contains("s4"));
- assertTrue(shortcutIds.contains("s5"));
- // Verifies pushDynamicShortcuts further persists shortcuts into AppSearch without
- // removing previous shortcuts when max number of shortcuts is reached.
- mManager.pushDynamicShortcut(makeShortcut("s6"));
- // Increasing the max number of shortcuts since number of results per page in AppSearch
- // is set to match the former.
- mService.updateConfigurationLocked(
- ShortcutService.ConfigConstants.KEY_MAX_SHORTCUTS + "=10,"
- + ShortcutService.ConfigConstants.KEY_SAVE_DELAY_MILLIS + "=1");
- shortcuts = getAllPersistedShortcuts();
- assertNotNull(shortcuts);
- assertEquals(6, shortcuts.size());
- shortcutIds = shortcuts.stream().map(ShortcutInfo::getId).collect(Collectors.toSet());
- assertTrue(shortcutIds.contains("s1"));
- assertTrue(shortcutIds.contains("s2"));
- assertTrue(shortcutIds.contains("s3"));
- assertTrue(shortcutIds.contains("s4"));
- assertTrue(shortcutIds.contains("s5"));
- assertTrue(shortcutIds.contains("s6"));
- }
-
- public void testRemoveDynamicShortcuts_RemovesShortcutsFromDisk() {
- if (!mService.isAppSearchEnabled()) {
- return;
- }
- setCaller(CALLING_PACKAGE_1, USER_10);
- mManager.setDynamicShortcuts(list(
- makeShortcut("s1"),
- makeShortcut("s2"),
- makeShortcut("s3"),
- makeShortcut("s4"),
- makeShortcut("s5")
- ));
-
- // Verifies removeDynamicShortcuts removes shortcuts from persistence layer
- mManager.removeDynamicShortcuts(list("s1"));
- final List<ShortcutInfo> shortcuts = getAllPersistedShortcuts();
- assertNotNull(shortcuts);
- assertEquals(4, shortcuts.size());
- final Set<String> shortcutIds =
- shortcuts.stream().map(ShortcutInfo::getId).collect(Collectors.toSet());
- assertTrue(shortcutIds.contains("s2"));
- assertTrue(shortcutIds.contains("s3"));
- assertTrue(shortcutIds.contains("s4"));
- assertTrue(shortcutIds.contains("s5"));
- }
-
- public void testRemoveLongLivedShortcuts_RemovesShortcutsFromDisk() {
- if (!mService.isAppSearchEnabled()) {
- return;
- }
- setCaller(CALLING_PACKAGE_1, USER_10);
- mManager.setDynamicShortcuts(list(
- makeShortcut("s1"),
- makeShortcut("s2"),
- makeShortcut("s3"),
- makeShortcut("s4"),
- makeShortcut("s5")
- ));
- mManager.removeDynamicShortcuts(list("s2"));
- final List<ShortcutInfo> shortcuts = getAllPersistedShortcuts();
- assertNotNull(shortcuts);
- assertEquals(4, shortcuts.size());
- final Set<String> shortcutIds =
- shortcuts.stream().map(ShortcutInfo::getId).collect(Collectors.toSet());
- assertTrue(shortcutIds.contains("s1"));
- assertTrue(shortcutIds.contains("s3"));
- assertTrue(shortcutIds.contains("s4"));
- assertTrue(shortcutIds.contains("s5"));
- }
-
- public void testDisableShortcuts_RemovesShortcutsFromDisk() {
- if (!mService.isAppSearchEnabled()) {
- return;
- }
- setCaller(CALLING_PACKAGE_1, USER_10);
- mManager.setDynamicShortcuts(list(
- makeShortcut("s1"),
- makeShortcut("s2"),
- makeShortcut("s3"),
- makeShortcut("s4"),
- makeShortcut("s5")
- ));
- // Verifies disableShortcuts removes shortcuts from persistence layer
- mManager.disableShortcuts(list("s3"));
- final List<ShortcutInfo> shortcuts = getAllPersistedShortcuts();
- assertNotNull(shortcuts);
- assertEquals(4, shortcuts.size());
- final Set<String> shortcutIds =
- shortcuts.stream().map(ShortcutInfo::getId).collect(Collectors.toSet());
- assertTrue(shortcutIds.contains("s1"));
- assertTrue(shortcutIds.contains("s2"));
- assertTrue(shortcutIds.contains("s4"));
- assertTrue(shortcutIds.contains("s5"));
- }
-
- public void testUpdateShortcuts_UpdateShortcutsOnDisk() {
- if (!mService.isAppSearchEnabled()) {
- return;
- }
- setCaller(CALLING_PACKAGE_1, USER_10);
- mManager.setDynamicShortcuts(list(
- makeShortcut("s1"),
- makeShortcut("s2"),
- makeShortcut("s3"),
- makeShortcut("s4"),
- makeShortcut("s5")
- ));
- // Verifies disableShortcuts removes shortcuts from persistence layer
- mManager.updateShortcuts(list(makeShortcutWithShortLabel("s3", "custom")));
- final List<ShortcutInfo> shortcuts = getAllPersistedShortcuts();
- assertNotNull(shortcuts);
- assertEquals(5, shortcuts.size());
- final Map<String, ShortcutInfo> map = shortcuts.stream()
- .collect(Collectors.toMap(ShortcutInfo::getId, Function.identity()));
- assertTrue(map.containsKey("s3"));
- assertEquals("custom", map.get("s3").getShortLabel());
- }
-
- public void testShortcutsExcludedFromLauncher_PersistedToDisk() {
- if (!mService.isAppSearchEnabled()) {
- return;
- }
- setCaller(CALLING_PACKAGE_1, USER_10);
- mManager.setDynamicShortcuts(list(
- makeShortcutExcludedFromLauncher("s1"),
- makeShortcutExcludedFromLauncher("s2"),
- makeShortcutExcludedFromLauncher("s3"),
- makeShortcutExcludedFromLauncher("s4"),
- makeShortcutExcludedFromLauncher("s5")
- ));
- final List<ShortcutInfo> shortcuts = getAllPersistedShortcuts();
- assertNotNull(shortcuts);
- assertEquals(5, shortcuts.size());
- final Map<String, ShortcutInfo> map = shortcuts.stream()
- .collect(Collectors.toMap(ShortcutInfo::getId, Function.identity()));
- assertTrue(map.containsKey("s3"));
- assertEquals("Title-s3", map.get("s3").getShortLabel());
- }
-
-
- private List<ShortcutInfo> getAllPersistedShortcuts() {
- try {
- SystemClock.sleep(5000);
- final AndroidFuture<List<ShortcutInfo>> future = new AndroidFuture<>();
- getPersistedShortcut(future);
- return future.get(10, TimeUnit.SECONDS);
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 94a4002..e3e9cc4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -361,12 +361,6 @@
return prepareStarter(launchFlags, mockGetRootTask, LAUNCH_MULTIPLE);
}
- private void setupImeWindow() {
- final WindowState imeWindow = createWindow(null, W_INPUT_METHOD,
- "mImeWindow", CURRENT_IME_UID);
- mDisplayContent.mInputMethodWindow = imeWindow;
- }
-
/**
* Creates a {@link ActivityStarter} with default parameters and necessary mocks.
*
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 57aacd3..5cd2a99 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -85,6 +85,7 @@
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
import static com.android.window.flags.Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING;
import static com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE;
+import static com.android.server.display.feature.flags.Flags.FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT;
import static com.google.common.truth.Truth.assertThat;
@@ -2879,6 +2880,43 @@
assertFalse(createNewDisplay().mAppCompatCameraPolicy.hasCameraCompatFreeformPolicy());
}
+ @EnableFlags(FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT)
+ @Test
+ public void testSetShouldShowSystemDecorations_defaultDisplay() {
+ DisplayContent dc = mWm.mRoot.getDisplayContent(DEFAULT_DISPLAY);
+
+ dc.onDisplayInfoChangeApplied();
+ assertTrue(dc.mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(dc));
+ }
+
+ @EnableFlags(FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT)
+ @Test
+ public void testSetShouldShowSystemDecorations_privateDisplay() {
+ final DisplayInfo displayInfo = new DisplayInfo(mDisplayInfo);
+ displayInfo.flags = FLAG_PRIVATE;
+ final DisplayContent dc = createNewDisplay(displayInfo);
+
+ dc.onDisplayInfoChangeApplied();
+ assertFalse(dc.mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(dc));
+ }
+
+ @EnableFlags(FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT)
+ @Test
+ public void testSetShouldShowSystemDecorations_nonDefaultNonPrivateDisplay() {
+ final DisplayInfo displayInfo = new DisplayInfo(mDisplayInfo);
+ displayInfo.displayId = DEFAULT_DISPLAY + 1;
+ final DisplayContent dc = createNewDisplay(displayInfo);
+
+ spyOn(dc.mDisplay);
+ doReturn(false).when(dc.mDisplay).canHostTasks();
+ dc.onDisplayInfoChangeApplied();
+ assertFalse(dc.mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(dc));
+
+ doReturn(true).when(dc.mDisplay).canHostTasks();
+ dc.onDisplayInfoChangeApplied();
+ assertTrue(dc.mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(dc));
+ }
+
private void removeRootTaskTests(Runnable runnable) {
final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
final Task rootTask1 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
diff --git a/services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java b/services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java
index c016c5e..de07168 100644
--- a/services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java
@@ -73,7 +73,7 @@
private static final float MID_REFRESH_RATE = 70;
private static final float LOW_REFRESH_RATE = 60;
WindowState createWindow(String name) {
- WindowState window = createWindow(null, TYPE_APPLICATION, name);
+ WindowState window = newWindowBuilder(name, TYPE_APPLICATION).build();
when(window.mWmService.mDisplayManagerInternal.getRefreshRateSwitchingType())
.thenReturn(DisplayManager.SWITCHING_TYPE_WITHIN_GROUPS);
return window;
diff --git a/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
index f70dceb..7d59f48 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
@@ -56,12 +56,13 @@
@Test
public void testTransparentControlTargetWindowCanShowIme() {
- final WindowState ime = createWindow(null, TYPE_INPUT_METHOD, "ime");
+ final WindowState ime = newWindowBuilder("ime", TYPE_INPUT_METHOD).build();
makeWindowVisibleAndDrawn(ime);
mImeProvider.setWindowContainer(ime, null, null);
- final WindowState appWin = createWindow(null, TYPE_APPLICATION, "app");
- final WindowState popup = createWindow(appWin, TYPE_APPLICATION, "popup");
+ final WindowState appWin = newWindowBuilder("app", TYPE_APPLICATION).build();
+ final WindowState popup = newWindowBuilder("popup", TYPE_APPLICATION).setParent(
+ appWin).build();
popup.mAttrs.format = PixelFormat.TRANSPARENT;
mDisplayContent.setImeLayeringTarget(appWin);
mDisplayContent.updateImeInputAndControlTarget(popup);
@@ -77,11 +78,11 @@
@Test
@RequiresFlagsDisabled(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)
public void testScheduleShowIme() {
- final WindowState ime = createWindow(null, TYPE_INPUT_METHOD, "ime");
+ final WindowState ime = newWindowBuilder("ime", TYPE_INPUT_METHOD).build();
makeWindowVisibleAndDrawn(ime);
mImeProvider.setWindowContainer(ime, null, null);
- final WindowState target = createWindow(null, TYPE_APPLICATION, "app");
+ final WindowState target = newWindowBuilder("app", TYPE_APPLICATION).build();
mDisplayContent.setImeLayeringTarget(target);
mDisplayContent.updateImeInputAndControlTarget(target);
performSurfacePlacementAndWaitForWindowAnimator();
@@ -105,14 +106,14 @@
@Test
@RequiresFlagsDisabled(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)
public void testScheduleShowIme_noInitialState() {
- final WindowState target = createWindow(null, TYPE_APPLICATION, "app");
+ final WindowState target = newWindowBuilder("app", TYPE_APPLICATION).build();
// Schedule before anything is ready.
mImeProvider.scheduleShowImePostLayout(target, ImeTracker.Token.empty());
assertFalse(mImeProvider.isScheduledAndReadyToShowIme());
assertFalse(mImeProvider.isImeShowing());
- final WindowState ime = createWindow(null, TYPE_INPUT_METHOD, "ime");
+ final WindowState ime = newWindowBuilder("ime", TYPE_INPUT_METHOD).build();
makeWindowVisibleAndDrawn(ime);
mImeProvider.setWindowContainer(ime, null, null);
@@ -133,11 +134,11 @@
@Test
@RequiresFlagsDisabled(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)
public void testScheduleShowIme_delayedAfterPrepareSurfaces() {
- final WindowState ime = createWindow(null, TYPE_INPUT_METHOD, "ime");
+ final WindowState ime = newWindowBuilder("ime", TYPE_INPUT_METHOD).build();
makeWindowVisibleAndDrawn(ime);
mImeProvider.setWindowContainer(ime, null, null);
- final WindowState target = createWindow(null, TYPE_APPLICATION, "app");
+ final WindowState target = newWindowBuilder("app", TYPE_APPLICATION).build();
mDisplayContent.setImeLayeringTarget(target);
mDisplayContent.updateImeInputAndControlTarget(target);
@@ -166,11 +167,11 @@
@Test
@RequiresFlagsDisabled(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)
public void testScheduleShowIme_delayedSurfacePlacement() {
- final WindowState ime = createWindow(null, TYPE_INPUT_METHOD, "ime");
+ final WindowState ime = newWindowBuilder("ime", TYPE_INPUT_METHOD).build();
makeWindowVisibleAndDrawn(ime);
mImeProvider.setWindowContainer(ime, null, null);
- final WindowState target = createWindow(null, TYPE_APPLICATION, "app");
+ final WindowState target = newWindowBuilder("app", TYPE_APPLICATION).build();
mDisplayContent.setImeLayeringTarget(target);
mDisplayContent.updateImeInputAndControlTarget(target);
@@ -191,7 +192,7 @@
@Test
public void testSetFrozen() {
- final WindowState ime = createWindow(null, TYPE_INPUT_METHOD, "ime");
+ final WindowState ime = newWindowBuilder("ime", TYPE_INPUT_METHOD).build();
makeWindowVisibleAndDrawn(ime);
mImeProvider.setWindowContainer(ime, null, null);
mImeProvider.setServerVisible(true);
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
index ee56210..6c5fe1d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
@@ -106,8 +106,9 @@
addStatusBar();
addNavigationBar();
- final WindowState win = createWindow(null, WINDOWING_MODE_FREEFORM,
- ACTIVITY_TYPE_STANDARD, TYPE_APPLICATION, mDisplayContent, "app");
+ final WindowState win = newWindowBuilder("app", TYPE_APPLICATION).setActivityType(
+ ACTIVITY_TYPE_STANDARD).setWindowingMode(WINDOWING_MODE_FREEFORM).setDisplay(
+ mDisplayContent).build();
final InsetsSourceControl[] controls = addWindowAndGetControlsForDispatch(win);
// The app must not control any system bars.
@@ -120,8 +121,9 @@
addStatusBar();
addNavigationBar();
- final WindowState win = createWindow(null, WINDOWING_MODE_FREEFORM,
- ACTIVITY_TYPE_STANDARD, TYPE_APPLICATION, mDisplayContent, "app");
+ final WindowState win = newWindowBuilder("app", TYPE_APPLICATION).setActivityType(
+ ACTIVITY_TYPE_STANDARD).setWindowingMode(WINDOWING_MODE_FREEFORM).setDisplay(
+ mDisplayContent).build();
win.setBounds(new Rect());
final InsetsSourceControl[] controls = addWindowAndGetControlsForDispatch(win);
@@ -136,8 +138,9 @@
addStatusBar();
addNavigationBar();
- final WindowState win = createWindow(null, WINDOWING_MODE_FREEFORM,
- ACTIVITY_TYPE_STANDARD, TYPE_APPLICATION, mDisplayContent, "app");
+ final WindowState win = newWindowBuilder("app", TYPE_APPLICATION).setActivityType(
+ ACTIVITY_TYPE_STANDARD).setWindowingMode(WINDOWING_MODE_FREEFORM).setDisplay(
+ mDisplayContent).build();
win.getTask().setBounds(new Rect(1, 1, 10, 10));
final InsetsSourceControl[] controls = addWindowAndGetControlsForDispatch(win);
@@ -582,7 +585,7 @@
private WindowState addNavigationBar() {
final Binder owner = new Binder();
- final WindowState win = createWindow(null, TYPE_NAVIGATION_BAR, "navBar");
+ final WindowState win = newWindowBuilder("navBar", TYPE_NAVIGATION_BAR).build();
win.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
win.mAttrs.providedInsets = new InsetsFrameProvider[] {
new InsetsFrameProvider(owner, 0, WindowInsets.Type.navigationBars()),
@@ -595,7 +598,7 @@
private WindowState addStatusBar() {
final Binder owner = new Binder();
- final WindowState win = createWindow(null, TYPE_STATUS_BAR, "statusBar");
+ final WindowState win = newWindowBuilder("statusBar", TYPE_STATUS_BAR).build();
win.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
win.mAttrs.providedInsets = new InsetsFrameProvider[] {
new InsetsFrameProvider(owner, 0, WindowInsets.Type.statusBars()),
@@ -607,7 +610,7 @@
}
private WindowState addWindow(int type, String name) {
- final WindowState win = createWindow(null, type, name);
+ final WindowState win = newWindowBuilder(name, type).build();
mDisplayContent.getDisplayPolicy().addWindowLw(win, win.mAttrs);
return win;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
index 79967b8..c30aa52 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
@@ -63,7 +63,7 @@
@Test
public void testPostLayout() {
- final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
+ final WindowState statusBar = newWindowBuilder("statusBar", TYPE_APPLICATION).build();
statusBar.setBounds(0, 0, 500, 1000);
statusBar.getFrame().set(0, 0, 500, 100);
statusBar.mHasSurface = true;
@@ -81,7 +81,7 @@
@Test
public void testPostLayout_invisible() {
- final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
+ final WindowState statusBar = newWindowBuilder("statusBar", TYPE_APPLICATION).build();
statusBar.setBounds(0, 0, 500, 1000);
statusBar.getFrame().set(0, 0, 500, 100);
mProvider.setWindowContainer(statusBar, null, null);
@@ -93,7 +93,7 @@
@Test
public void testPostLayout_frameProvider() {
- final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
+ final WindowState statusBar = newWindowBuilder("statusBar", TYPE_APPLICATION).build();
statusBar.getFrame().set(0, 0, 500, 100);
statusBar.mHasSurface = true;
mProvider.setWindowContainer(statusBar,
@@ -108,8 +108,8 @@
@Test
public void testUpdateControlForTarget() {
- final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
- final WindowState target = createWindow(null, TYPE_APPLICATION, "target");
+ final WindowState statusBar = newWindowBuilder("statusBar", TYPE_APPLICATION).build();
+ final WindowState target = newWindowBuilder("target", TYPE_APPLICATION).build();
statusBar.getFrame().set(0, 0, 500, 100);
// We must not have control or control target before we have the insets source window.
@@ -153,8 +153,8 @@
@Test
public void testUpdateControlForFakeTarget() {
- final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
- final WindowState target = createWindow(null, TYPE_APPLICATION, "target");
+ final WindowState statusBar = newWindowBuilder("statusBar", TYPE_APPLICATION).build();
+ final WindowState target = newWindowBuilder("target", TYPE_APPLICATION).build();
statusBar.getFrame().set(0, 0, 500, 100);
mProvider.setWindowContainer(statusBar, null, null);
mProvider.updateFakeControlTarget(target);
@@ -166,10 +166,10 @@
@Test
public void testGetLeash() {
- final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
- final WindowState target = createWindow(null, TYPE_APPLICATION, "target");
- final WindowState fakeTarget = createWindow(null, TYPE_APPLICATION, "fakeTarget");
- final WindowState otherTarget = createWindow(null, TYPE_APPLICATION, "otherTarget");
+ final WindowState statusBar = newWindowBuilder("statusBar", TYPE_APPLICATION).build();
+ final WindowState target = newWindowBuilder("target", TYPE_APPLICATION).build();
+ final WindowState fakeTarget = newWindowBuilder("fakeTarget", TYPE_APPLICATION).build();
+ final WindowState otherTarget = newWindowBuilder("otherTarget", TYPE_APPLICATION).build();
statusBar.getFrame().set(0, 0, 500, 100);
// We must not have control or control target before we have the insets source window,
@@ -208,7 +208,7 @@
@Test
public void testUpdateSourceFrame() {
- final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
+ final WindowState statusBar = newWindowBuilder("statusBar", TYPE_APPLICATION).build();
mProvider.setWindowContainer(statusBar, null, null);
statusBar.setBounds(0, 0, 500, 1000);
@@ -238,7 +238,7 @@
@Test
public void testUpdateSourceFrameForIme() {
- final WindowState inputMethod = createWindow(null, TYPE_INPUT_METHOD, "inputMethod");
+ final WindowState inputMethod = newWindowBuilder("inputMethod", TYPE_INPUT_METHOD).build();
inputMethod.getFrame().set(new Rect(0, 400, 500, 500));
@@ -262,9 +262,9 @@
@Test
public void testUpdateInsetsControlPosition() {
- final WindowState target = createWindow(null, TYPE_APPLICATION, "target");
+ final WindowState target = newWindowBuilder("target", TYPE_APPLICATION).build();
- final WindowState ime1 = createWindow(null, TYPE_INPUT_METHOD, "ime1");
+ final WindowState ime1 = newWindowBuilder("ime1", TYPE_INPUT_METHOD).build();
ime1.getFrame().set(new Rect(0, 0, 0, 0));
mImeProvider.setWindowContainer(ime1, null, null);
mImeProvider.updateControlForTarget(target, false /* force */, null /* statsToken */);
@@ -272,7 +272,7 @@
mImeProvider.updateInsetsControlPosition(ime1);
assertEquals(new Point(0, 400), mImeProvider.getControl(target).getSurfacePosition());
- final WindowState ime2 = createWindow(null, TYPE_INPUT_METHOD, "ime2");
+ final WindowState ime2 = newWindowBuilder("ime2", TYPE_INPUT_METHOD).build();
ime2.getFrame().set(new Rect(0, 0, 0, 0));
mImeProvider.setWindowContainer(ime2, null, null);
mImeProvider.updateControlForTarget(target, false /* force */, null /* statsToken */);
@@ -283,8 +283,8 @@
@Test
public void testSetRequestedVisibleTypes() {
- final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
- final WindowState target = createWindow(null, TYPE_APPLICATION, "target");
+ final WindowState statusBar = newWindowBuilder("statusBar", TYPE_APPLICATION).build();
+ final WindowState target = newWindowBuilder("target", TYPE_APPLICATION).build();
statusBar.getFrame().set(0, 0, 500, 100);
mProvider.setWindowContainer(statusBar, null, null);
mProvider.updateControlForTarget(target, false /* force */, null /* statsToken */);
@@ -295,8 +295,8 @@
@Test
public void testSetRequestedVisibleTypes_noControl() {
- final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
- final WindowState target = createWindow(null, TYPE_APPLICATION, "target");
+ final WindowState statusBar = newWindowBuilder("statusBar", TYPE_APPLICATION).build();
+ final WindowState target = newWindowBuilder("target", TYPE_APPLICATION).build();
statusBar.getFrame().set(0, 0, 500, 100);
mProvider.setWindowContainer(statusBar, null, null);
target.setRequestedVisibleTypes(0, statusBars());
@@ -306,7 +306,7 @@
@Test
public void testInsetGeometries() {
- final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
+ final WindowState statusBar = newWindowBuilder("statusBar", TYPE_APPLICATION).build();
statusBar.getFrame().set(0, 0, 500, 100);
statusBar.mHasSurface = true;
mProvider.setWindowContainer(statusBar, null, null);
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index 66a66a1..973c8d0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -76,9 +76,9 @@
@Test
public void testStripForDispatch_navBar() {
- final WindowState navBar = createWindow(null, TYPE_APPLICATION, "navBar");
- final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
- final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime");
+ final WindowState navBar = newWindowBuilder("navBar", TYPE_APPLICATION).build();
+ final WindowState statusBar = newWindowBuilder("statusBar", TYPE_APPLICATION).build();
+ final WindowState ime = newWindowBuilder("ime", TYPE_APPLICATION).build();
// IME cannot be the IME target.
ime.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
@@ -96,9 +96,9 @@
@Test
public void testStripForDispatch_pip() {
- final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
- final WindowState navBar = createWindow(null, TYPE_APPLICATION, "navBar");
- final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+ final WindowState statusBar = newWindowBuilder("statusBar", TYPE_APPLICATION).build();
+ final WindowState navBar = newWindowBuilder("navBar", TYPE_APPLICATION).build();
+ final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build();
getController().getOrCreateSourceProvider(ID_STATUS_BAR, statusBars())
.setWindowContainer(statusBar, null, null);
@@ -113,9 +113,9 @@
@Test
public void testStripForDispatch_freeform() {
- final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
- final WindowState navBar = createWindow(null, TYPE_APPLICATION, "navBar");
- final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+ final WindowState statusBar = newWindowBuilder("statusBar", TYPE_APPLICATION).build();
+ final WindowState navBar = newWindowBuilder("navBar", TYPE_APPLICATION).build();
+ final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build();
getController().getOrCreateSourceProvider(ID_STATUS_BAR, statusBars())
.setWindowContainer(statusBar, null, null);
@@ -129,9 +129,9 @@
@Test
public void testStripForDispatch_multiwindow_alwaysOnTop() {
- final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
- final WindowState navBar = createWindow(null, TYPE_APPLICATION, "navBar");
- final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+ final WindowState statusBar = newWindowBuilder("statusBar", TYPE_APPLICATION).build();
+ final WindowState navBar = newWindowBuilder("navBar", TYPE_APPLICATION).build();
+ final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build();
getController().getOrCreateSourceProvider(ID_STATUS_BAR, statusBars())
.setWindowContainer(statusBar, null, null);
@@ -150,8 +150,8 @@
getController().getOrCreateSourceProvider(ID_IME, ime())
.setWindowContainer(mImeWindow, null, null);
- final WindowState app1 = createWindow(null, TYPE_APPLICATION, "app1");
- final WindowState app2 = createWindow(null, TYPE_APPLICATION, "app2");
+ final WindowState app1 = newWindowBuilder("app1", TYPE_APPLICATION).build();
+ final WindowState app2 = newWindowBuilder("app2", TYPE_APPLICATION).build();
app1.mAboveInsetsState.addSource(getController().getRawInsetsState().peekSource(ID_IME));
@@ -166,7 +166,7 @@
getController().getOrCreateSourceProvider(ID_IME, ime())
.setWindowContainer(mImeWindow, null, null);
- final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+ final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build();
app.mAboveInsetsState.getOrCreateSource(ID_IME, ime())
.setVisible(true)
.setFrame(mImeWindow.getFrame());
@@ -181,7 +181,7 @@
getController().getOrCreateSourceProvider(ID_IME, ime())
.setWindowContainer(mImeWindow, null, null);
- final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+ final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build();
getController().getRawInsetsState().setSourceVisible(ID_IME, true);
assertFalse(app.getInsetsState().isSourceOrDefaultVisible(ID_IME, ime()));
@@ -193,7 +193,7 @@
// This can be the IME z-order target while app cannot be the IME z-order target.
// This is also the only IME control target in this test, so IME won't be invisible caused
// by the control-target change.
- final WindowState base = createWindow(null, TYPE_APPLICATION, "base");
+ final WindowState base = newWindowBuilder("base", TYPE_APPLICATION).build();
mDisplayContent.updateImeInputAndControlTarget(base);
// Make IME and stay visible during the test.
@@ -210,7 +210,7 @@
}
// Send our spy window (app) into the system so that we can detect the invocation.
- final WindowState win = createWindow(null, TYPE_APPLICATION, "app");
+ final WindowState win = newWindowBuilder("app", TYPE_APPLICATION).build();
win.setHasSurface(true);
final WindowToken parent = win.mToken;
parent.removeChild(win);
@@ -250,8 +250,9 @@
getController().getOrCreateSourceProvider(ID_IME, ime())
.setWindowContainer(mImeWindow, null, null);
- final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
- final WindowState child = createWindow(app, TYPE_APPLICATION, "child");
+ final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build();
+ final WindowState child = newWindowBuilder("child", TYPE_APPLICATION).setParent(
+ app).build();
app.mAboveInsetsState.set(getController().getRawInsetsState());
child.mAboveInsetsState.set(getController().getRawInsetsState());
child.mAttrs.flags |= FLAG_ALT_FOCUSABLE_IM;
@@ -271,8 +272,9 @@
getController().getOrCreateSourceProvider(ID_IME, ime())
.setWindowContainer(mImeWindow, null, null);
- final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
- final WindowState child = createWindow(app, TYPE_APPLICATION, "child");
+ final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build();
+ final WindowState child = newWindowBuilder("child", TYPE_APPLICATION).setParent(
+ app).build();
app.mAboveInsetsState.addSource(getController().getRawInsetsState().peekSource(ID_IME));
child.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
child.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
@@ -288,8 +290,8 @@
@Test
public void testImeForDispatch() {
- final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
- final WindowState ime = createWindow(null, TYPE_INPUT_METHOD, "ime");
+ final WindowState statusBar = newWindowBuilder("statusBar", TYPE_APPLICATION).build();
+ final WindowState ime = newWindowBuilder("ime", TYPE_INPUT_METHOD).build();
makeWindowVisible(statusBar);
@@ -318,11 +320,11 @@
@Test
public void testBarControllingWinChanged() {
- final WindowState navBar = createWindow(null, TYPE_APPLICATION, "navBar");
- final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
- final WindowState climateBar = createWindow(null, TYPE_APPLICATION, "climateBar");
- final WindowState extraNavBar = createWindow(null, TYPE_APPLICATION, "extraNavBar");
- final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+ final WindowState navBar = newWindowBuilder("navBar", TYPE_APPLICATION).build();
+ final WindowState statusBar = newWindowBuilder("statusBar", TYPE_APPLICATION).build();
+ final WindowState climateBar = newWindowBuilder("climateBar", TYPE_APPLICATION).build();
+ final WindowState extraNavBar = newWindowBuilder("extraNavBar", TYPE_APPLICATION).build();
+ final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build();
getController().getOrCreateSourceProvider(ID_STATUS_BAR, statusBars())
.setWindowContainer(statusBar, null, null);
getController().getOrCreateSourceProvider(ID_NAVIGATION_BAR, navigationBars())
@@ -338,8 +340,8 @@
@Test
public void testControlRevoked() {
- final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
- final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+ final WindowState statusBar = newWindowBuilder("statusBar", TYPE_APPLICATION).build();
+ final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build();
getController().getOrCreateSourceProvider(ID_STATUS_BAR, statusBars())
.setWindowContainer(statusBar, null, null);
getController().onBarControlTargetChanged(app, null, null, null);
@@ -350,8 +352,8 @@
@Test
public void testControlRevoked_animation() {
- final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
- final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+ final WindowState statusBar = newWindowBuilder("statusBar", TYPE_APPLICATION).build();
+ final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build();
getController().getOrCreateSourceProvider(ID_STATUS_BAR, statusBars())
.setWindowContainer(statusBar, null, null);
getController().onBarControlTargetChanged(app, null, null, null);
@@ -362,19 +364,20 @@
@Test
public void testControlTargetChangedWhileProviderHasNoWindow() {
- final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+ final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build();
final InsetsSourceProvider provider = getController().getOrCreateSourceProvider(
ID_STATUS_BAR, statusBars());
getController().onBarControlTargetChanged(app, null, null, null);
assertNull(getController().getControlsForDispatch(app));
- provider.setWindowContainer(createWindow(null, TYPE_APPLICATION, "statusBar"), null, null);
+ provider.setWindowContainer(newWindowBuilder("statusBar", TYPE_APPLICATION).build(), null,
+ null);
assertNotNull(getController().getControlsForDispatch(app));
}
@Test
public void testTransientVisibilityOfFixedRotationState() {
- final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
- final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+ final WindowState statusBar = newWindowBuilder("statusBar", TYPE_APPLICATION).build();
+ final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build();
final InsetsSourceProvider provider = getController()
.getOrCreateSourceProvider(ID_STATUS_BAR, statusBars());
provider.setWindowContainer(statusBar, null, null);
@@ -494,7 +497,8 @@
@Test
public void testUpdateAboveInsetsState_imeTargetOnScreenBehavior() {
final WindowToken imeToken = createTestWindowToken(TYPE_INPUT_METHOD, mDisplayContent);
- final WindowState ime = createWindow(null, TYPE_INPUT_METHOD, imeToken, "ime");
+ final WindowState ime = newWindowBuilder("ime", TYPE_INPUT_METHOD).setWindowToken(
+ imeToken).build();
final WindowState app = createTestWindow("app");
getController().getOrCreateSourceProvider(ID_IME, ime())
@@ -538,10 +542,10 @@
@Test
public void testDispatchGlobalInsets() {
- final WindowState navBar = createWindow(null, TYPE_APPLICATION, "navBar");
+ final WindowState navBar = newWindowBuilder("navBar", TYPE_APPLICATION).build();
getController().getOrCreateSourceProvider(ID_NAVIGATION_BAR, navigationBars())
.setWindowContainer(navBar, null, null);
- final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+ final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build();
assertNull(app.getInsetsState().peekSource(ID_NAVIGATION_BAR));
app.mAttrs.receiveInsetsIgnoringZOrder = true;
assertNotNull(app.getInsetsState().peekSource(ID_NAVIGATION_BAR));
@@ -580,8 +584,8 @@
@Test
public void testHasPendingControls() {
- final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
- final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+ final WindowState statusBar = newWindowBuilder("statusBar", TYPE_APPLICATION).build();
+ final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build();
getController().getOrCreateSourceProvider(ID_STATUS_BAR, statusBars())
.setWindowContainer(statusBar, null, null);
// No controls dispatched yet.
@@ -598,7 +602,7 @@
/** Creates a window which is associated with ActivityRecord. */
private WindowState createTestWindow(String name) {
- final WindowState win = createWindow(null, TYPE_APPLICATION, name);
+ final WindowState win = newWindowBuilder(name, TYPE_APPLICATION).build();
win.setHasSurface(true);
spyOn(win);
return win;
@@ -606,7 +610,7 @@
/** Creates a non-activity window. */
private WindowState createNonAppWindow(String name) {
- final WindowState win = createWindow(null, LAST_APPLICATION_WINDOW + 1, name);
+ final WindowState win = newWindowBuilder(name, LAST_APPLICATION_WINDOW + 1).build();
win.setHasSurface(true);
spyOn(win);
return win;
diff --git a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
index 73e5f58..45436e4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
@@ -108,7 +108,7 @@
}
WindowState createWindow(String name) {
- WindowState window = createWindow(null, TYPE_BASE_APPLICATION, name);
+ WindowState window = newWindowBuilder(name, TYPE_BASE_APPLICATION).build();
when(window.getDisplayInfo()).thenReturn(mDisplayInfo);
when(window.mWmService.mDisplayManagerInternal.getRefreshRateSwitchingType())
.thenReturn(DisplayManager.SWITCHING_TYPE_WITHIN_GROUPS);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index c5cbedb..20381ba2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -114,8 +114,8 @@
}
private WindowState createAppOverlayWindow() {
- final WindowState win = createWindow(null /* parent */, TYPE_APPLICATION_OVERLAY,
- "testOverlayWindow");
+ final WindowState win = newWindowBuilder("testOverlayWindow",
+ TYPE_APPLICATION_OVERLAY).build();
win.mActivityRecord = null;
win.mHasSurface = true;
return win;
@@ -123,7 +123,7 @@
@Test
public void testForwardsShowBackdrop() throws Exception {
- final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+ final WindowState win = createTestWindow();
mDisplayContent.mOpeningApps.add(win.mActivityRecord);
final WindowState overlayWin = createAppOverlayWindow();
try {
@@ -156,7 +156,7 @@
@Test
public void testRun() throws Exception {
- final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+ final WindowState win = createTestWindow();
mDisplayContent.mOpeningApps.add(win.mActivityRecord);
final WindowState overlayWin = createAppOverlayWindow();
try {
@@ -200,7 +200,7 @@
@Test
public void testCancel() throws Exception {
- final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+ final WindowState win = createTestWindow();
final AnimationAdapter adapter = mController.createRemoteAnimationRecord(
win.mActivityRecord,
new Point(50, 100), null, new Rect(50, 100, 150, 150), null, false).mAdapter;
@@ -214,7 +214,7 @@
@Test
public void testTimeout() throws Exception {
- final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+ final WindowState win = createTestWindow();
final AnimationAdapter adapter = mController.createRemoteAnimationRecord(
win.mActivityRecord,
new Point(50, 100), null, new Rect(50, 100, 150, 150), null, false).mAdapter;
@@ -234,8 +234,7 @@
public void testTimeout_scaled() throws Exception {
try {
mWm.setAnimationScale(2, 5.0f);
- final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION,
- "testWin");
+ final WindowState win = createTestWindow();
final AnimationAdapter adapter = mController.createRemoteAnimationRecord(
win.mActivityRecord, new Point(50, 100), null, new Rect(50, 100, 150, 150),
null, false).mAdapter;
@@ -268,7 +267,7 @@
@Test
public void testNotReallyStarted() throws Exception {
- final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+ final WindowState win = createTestWindow();
mController.createRemoteAnimationRecord(win.mActivityRecord,
new Point(50, 100), null, new Rect(50, 100, 150, 150), null, false);
mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
@@ -278,8 +277,8 @@
@Test
public void testOneNotStarted() throws Exception {
- final WindowState win1 = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin1");
- final WindowState win2 = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin2");
+ final WindowState win1 = newWindowBuilder("testWin1", TYPE_BASE_APPLICATION).build();
+ final WindowState win2 = newWindowBuilder("testWin2", TYPE_BASE_APPLICATION).build();
mController.createRemoteAnimationRecord(win1.mActivityRecord,
new Point(50, 100), null, new Rect(50, 100, 150, 150), null, false);
final AnimationAdapter adapter = mController.createRemoteAnimationRecord(
@@ -306,7 +305,7 @@
@Test
public void testRemovedBeforeStarted() throws Exception {
- final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+ final WindowState win = createTestWindow();
final AnimationAdapter adapter = mController.createRemoteAnimationRecord(
win.mActivityRecord,
new Point(50, 100), null, new Rect(50, 100, 150, 150), null, false).mAdapter;
@@ -322,7 +321,7 @@
@Test
public void testOpeningTaskWithTopFinishingActivity() {
- final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "win");
+ final WindowState win = createTestWindow();
final Task task = win.getTask();
final ActivityRecord topFinishing = new ActivityBuilder(mAtm).setTask(task).build();
// Now the task contains:
@@ -348,7 +347,7 @@
@Test
public void testChangeToSmallerSize() throws Exception {
- final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+ final WindowState win = createTestWindow();
mDisplayContent.mChangingContainers.add(win.mActivityRecord);
try {
final RemoteAnimationRecord record = mController.createRemoteAnimationRecord(
@@ -402,7 +401,7 @@
@Test
public void testChangeTolargerSize() throws Exception {
- final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+ final WindowState win = createTestWindow();
mDisplayContent.mChangingContainers.add(win.mActivityRecord);
try {
final RemoteAnimationRecord record = mController.createRemoteAnimationRecord(
@@ -456,7 +455,7 @@
@Test
public void testChangeToDifferentPosition() throws Exception {
- final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+ final WindowState win = createTestWindow();
mDisplayContent.mChangingContainers.add(win.mActivityRecord);
try {
final RemoteAnimationRecord record = mController.createRemoteAnimationRecord(
@@ -515,7 +514,7 @@
true, mDisplayContent, true /* ownerCanManageAppTokens */);
spyOn(mDisplayContent.mWallpaperController);
doReturn(true).when(mDisplayContent.mWallpaperController).isWallpaperVisible();
- final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+ final WindowState win = createTestWindow();
mDisplayContent.mOpeningApps.add(win.mActivityRecord);
try {
final AnimationAdapter adapter = mController.createRemoteAnimationRecord(
@@ -548,7 +547,7 @@
true, mDisplayContent, true /* ownerCanManageAppTokens */);
spyOn(mDisplayContent.mWallpaperController);
doReturn(true).when(mDisplayContent.mWallpaperController).isWallpaperVisible();
- final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+ final WindowState win = createTestWindow();
mDisplayContent.mOpeningApps.add(win.mActivityRecord);
try {
final AnimationAdapter adapter = mController.createRemoteAnimationRecord(
@@ -581,7 +580,7 @@
@Test
public void testNonAppIncluded_keygaurdGoingAway() throws Exception {
- final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+ final WindowState win = createTestWindow();
mDisplayContent.mOpeningApps.add(win.mActivityRecord);
// Add overlay window hidden by the keyguard.
final WindowState overlayWin = createAppOverlayWindow();
@@ -631,7 +630,7 @@
true, mDisplayContent, true /* ownerCanManageAppTokens */);
spyOn(mDisplayContent.mWallpaperController);
doReturn(true).when(mDisplayContent.mWallpaperController).isWallpaperVisible();
- final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+ final WindowState win = createTestWindow();
mDisplayContent.mOpeningApps.add(win.mActivityRecord);
// Add overlay window hidden by the keyguard.
final WindowState overlayWin = createAppOverlayWindow();
@@ -729,9 +728,9 @@
}
private AnimationAdapter setupForNonAppTargetNavBar(int transit, boolean shouldAttachNavBar) {
- final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+ final WindowState win = createTestWindow();
mDisplayContent.mOpeningApps.add(win.mActivityRecord);
- final WindowState navBar = createWindow(null, TYPE_NAVIGATION_BAR, "NavigationBar");
+ final WindowState navBar = newWindowBuilder("NavigationBar", TYPE_NAVIGATION_BAR).build();
mDisplayContent.getDisplayPolicy().addWindowLw(navBar, navBar.mAttrs);
final DisplayPolicy policy = mDisplayContent.getDisplayPolicy();
spyOn(policy);
@@ -751,4 +750,8 @@
verify(binder, atLeast(0)).asBinder();
verifyNoMoreInteractions(binder);
}
+
+ private WindowState createTestWindow() {
+ return newWindowBuilder("testWin", TYPE_BASE_APPLICATION).build();
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 6a738ae5..9d9f24c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -673,7 +673,8 @@
mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE);
- final WindowState window = createWindow(null, TYPE_BASE_APPLICATION, mActivity, "window");
+ final WindowState window = newWindowBuilder("window", TYPE_BASE_APPLICATION).setWindowToken(
+ mActivity).build();
assertEquals(window, mActivity.findMainWindow());
@@ -3996,8 +3997,8 @@
resizeDisplay(display, 2200, 2280);
display.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
// Simulate insets, final app bounds are (0, 0, 2200, 2130) - landscape.
- final WindowState navbar = createWindow(null, TYPE_NAVIGATION_BAR, mDisplayContent,
- "navbar");
+ final WindowState navbar = newWindowBuilder("navbar", TYPE_NAVIGATION_BAR).setDisplay(
+ mDisplayContent).build();
final Binder owner = new Binder();
navbar.mAttrs.providedInsets = new InsetsFrameProvider[] {
new InsetsFrameProvider(owner, 0, WindowInsets.Type.navigationBars())
@@ -4030,8 +4031,8 @@
resizeDisplay(display, 2200, 2280);
display.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
// Simulate taskbar, final app bounds are (0, 0, 2200, 2130) - landscape
- final WindowState navbar = createWindow(null, TYPE_NAVIGATION_BAR, mDisplayContent,
- "navbar");
+ final WindowState navbar = newWindowBuilder("navbar", TYPE_NAVIGATION_BAR).setDisplay(
+ mDisplayContent).build();
final Binder owner = new Binder();
navbar.mAttrs.providedInsets = new InsetsFrameProvider[] {
new InsetsFrameProvider(owner, 0, WindowInsets.Type.navigationBars())
@@ -4059,8 +4060,8 @@
resizeDisplay(dc, 2200, 2280);
dc.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
// Simulate taskbar, final app bounds are (0, 0, 2200, 2130) - landscape
- final WindowState navbar = createWindow(null, TYPE_NAVIGATION_BAR, mDisplayContent,
- "navbar");
+ final WindowState navbar = newWindowBuilder("navbar", TYPE_NAVIGATION_BAR).setDisplay(
+ mDisplayContent).build();
final Binder owner = new Binder();
navbar.mAttrs.providedInsets = new InsetsFrameProvider[] {
new InsetsFrameProvider(owner, 0, WindowInsets.Type.navigationBars())
diff --git a/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java b/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java
index 1c32980..da5210c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java
@@ -152,9 +152,9 @@
final Task task = taskRoot.getTask();
final ActivityRecord translucentTop = new ActivityBuilder(mAtm).setTask(task)
.setActivityTheme(android.R.style.Theme_Translucent).build();
- createWindow(null, TYPE_BASE_APPLICATION, taskRoot, "win");
- final WindowState startingWindow = createWindow(null, TYPE_APPLICATION_STARTING,
- translucentTop, "starting");
+ newWindowBuilder("win", TYPE_BASE_APPLICATION).setWindowToken(taskRoot).build();
+ final WindowState startingWindow = newWindowBuilder("starting",
+ TYPE_APPLICATION_STARTING).setWindowToken(translucentTop).build();
startingWindow.mStartingData = new SnapshotStartingData(mWm, null, 0);
task.mSharedStartingData = startingWindow.mStartingData;
task.prepareSync();
@@ -355,7 +355,7 @@
assertEquals(SYNC_STATE_NONE, botChildWC.mSyncState);
// If the appearance of window won't change after reparenting, its sync state can be kept.
- final WindowState w = createWindow(null, TYPE_BASE_APPLICATION, "win");
+ final WindowState w = newWindowBuilder("win", TYPE_BASE_APPLICATION).build();
parentWC.onRequestedOverrideConfigurationChanged(w.getConfiguration());
w.reparent(botChildWC, POSITION_TOP);
parentWC.prepareSync();
@@ -435,7 +435,7 @@
@Test
public void testNonBlastMethod() {
- mAppWindow = createWindow(null, TYPE_BASE_APPLICATION, "mAppWindow");
+ mAppWindow = newWindowBuilder("mAppWindow", TYPE_BASE_APPLICATION).build();
final BLASTSyncEngine bse = createTestBLASTSyncEngine();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
index 35a2546..c0f251e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
@@ -52,6 +52,7 @@
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.clearInvocations;
@@ -1070,94 +1071,106 @@
@EnableFlags(Flags.FLAG_ALLOW_MULTIPLE_ADJACENT_TASK_FRAGMENTS)
@Test
- public void testSetAdjacentTaskFragments() {
+ public void testAdjacentSetForTaskFragments() {
final Task task = createTask(mDisplayContent);
final TaskFragment tf0 = createTaskFragmentWithActivity(task);
final TaskFragment tf1 = createTaskFragmentWithActivity(task);
final TaskFragment tf2 = createTaskFragmentWithActivity(task);
- final TaskFragment.AdjacentSet adjacentTfs = new TaskFragment.AdjacentSet(tf0, tf1, tf2);
- assertFalse(tf0.hasAdjacentTaskFragment());
- tf0.setAdjacentTaskFragments(adjacentTfs);
+ // Can have two TFs adjacent,
+ new TaskFragment.AdjacentSet(tf0, tf1);
- assertSame(adjacentTfs, tf0.getAdjacentTaskFragments());
- assertSame(adjacentTfs, tf1.getAdjacentTaskFragments());
- assertSame(adjacentTfs, tf2.getAdjacentTaskFragments());
- assertTrue(tf0.hasAdjacentTaskFragment());
- assertTrue(tf1.hasAdjacentTaskFragment());
- assertTrue(tf2.hasAdjacentTaskFragment());
+ // 3+ TFs adjacent is not yet supported.
+ assertThrows(IllegalArgumentException.class,
+ () -> new TaskFragment.AdjacentSet(tf0, tf1, tf2));
+ }
- final TaskFragment.AdjacentSet adjacentTfs2 = new TaskFragment.AdjacentSet(tf0, tf1);
- tf0.setAdjacentTaskFragments(adjacentTfs2);
+ @EnableFlags(Flags.FLAG_ALLOW_MULTIPLE_ADJACENT_TASK_FRAGMENTS)
+ @Test
+ public void testSetAdjacentTaskFragments() {
+ final Task task0 = createTask(mDisplayContent);
+ final Task task1 = createTask(mDisplayContent);
+ final Task task2 = createTask(mDisplayContent);
+ final TaskFragment.AdjacentSet adjTasks = new TaskFragment.AdjacentSet(task0, task1, task2);
+ assertFalse(task0.hasAdjacentTaskFragment());
- assertSame(adjacentTfs2, tf0.getAdjacentTaskFragments());
- assertSame(adjacentTfs2, tf1.getAdjacentTaskFragments());
- assertNull(tf2.getAdjacentTaskFragments());
- assertTrue(tf0.hasAdjacentTaskFragment());
- assertTrue(tf1.hasAdjacentTaskFragment());
- assertFalse(tf2.hasAdjacentTaskFragment());
+ task0.setAdjacentTaskFragments(adjTasks);
+
+ assertSame(adjTasks, task0.getAdjacentTaskFragments());
+ assertSame(adjTasks, task1.getAdjacentTaskFragments());
+ assertSame(adjTasks, task2.getAdjacentTaskFragments());
+ assertTrue(task0.hasAdjacentTaskFragment());
+ assertTrue(task1.hasAdjacentTaskFragment());
+ assertTrue(task2.hasAdjacentTaskFragment());
+
+ final TaskFragment.AdjacentSet adjTasks2 = new TaskFragment.AdjacentSet(task0, task1);
+ task0.setAdjacentTaskFragments(adjTasks2);
+
+ assertSame(adjTasks2, task0.getAdjacentTaskFragments());
+ assertSame(adjTasks2, task1.getAdjacentTaskFragments());
+ assertNull(task2.getAdjacentTaskFragments());
+ assertTrue(task0.hasAdjacentTaskFragment());
+ assertTrue(task1.hasAdjacentTaskFragment());
+ assertFalse(task2.hasAdjacentTaskFragment());
}
@EnableFlags(Flags.FLAG_ALLOW_MULTIPLE_ADJACENT_TASK_FRAGMENTS)
@Test
public void testClearAdjacentTaskFragments() {
- final Task task = createTask(mDisplayContent);
- final TaskFragment tf0 = createTaskFragmentWithActivity(task);
- final TaskFragment tf1 = createTaskFragmentWithActivity(task);
- final TaskFragment tf2 = createTaskFragmentWithActivity(task);
- final TaskFragment.AdjacentSet adjacentTfs = new TaskFragment.AdjacentSet(tf0, tf1, tf2);
- tf0.setAdjacentTaskFragments(adjacentTfs);
+ final Task task0 = createTask(mDisplayContent);
+ final Task task1 = createTask(mDisplayContent);
+ final Task task2 = createTask(mDisplayContent);
+ final TaskFragment.AdjacentSet adjTasks = new TaskFragment.AdjacentSet(task0, task1, task2);
+ task0.setAdjacentTaskFragments(adjTasks);
- tf0.clearAdjacentTaskFragments();
+ task0.clearAdjacentTaskFragments();
- assertNull(tf0.getAdjacentTaskFragments());
- assertNull(tf1.getAdjacentTaskFragments());
- assertNull(tf2.getAdjacentTaskFragments());
- assertFalse(tf0.hasAdjacentTaskFragment());
- assertFalse(tf1.hasAdjacentTaskFragment());
- assertFalse(tf2.hasAdjacentTaskFragment());
+ assertNull(task0.getAdjacentTaskFragments());
+ assertNull(task1.getAdjacentTaskFragments());
+ assertNull(task2.getAdjacentTaskFragments());
+ assertFalse(task0.hasAdjacentTaskFragment());
+ assertFalse(task1.hasAdjacentTaskFragment());
+ assertFalse(task2.hasAdjacentTaskFragment());
}
@EnableFlags(Flags.FLAG_ALLOW_MULTIPLE_ADJACENT_TASK_FRAGMENTS)
@Test
public void testRemoveFromAdjacentTaskFragments() {
- final Task task = createTask(mDisplayContent);
- final TaskFragment tf0 = createTaskFragmentWithActivity(task);
- final TaskFragment tf1 = createTaskFragmentWithActivity(task);
- final TaskFragment tf2 = createTaskFragmentWithActivity(task);
- final TaskFragment.AdjacentSet adjacentTfs = new TaskFragment.AdjacentSet(tf0, tf1, tf2);
- tf0.setAdjacentTaskFragments(adjacentTfs);
+ final Task task0 = createTask(mDisplayContent);
+ final Task task1 = createTask(mDisplayContent);
+ final Task task2 = createTask(mDisplayContent);
+ final TaskFragment.AdjacentSet adjTasks = new TaskFragment.AdjacentSet(task0, task1, task2);
+ task0.setAdjacentTaskFragments(adjTasks);
- tf0.removeFromAdjacentTaskFragments();
+ task0.removeFromAdjacentTaskFragments();
- assertNull(tf0.getAdjacentTaskFragments());
- assertSame(adjacentTfs, tf1.getAdjacentTaskFragments());
- assertSame(adjacentTfs, tf2.getAdjacentTaskFragments());
- assertFalse(adjacentTfs.contains(tf0));
- assertTrue(tf1.isAdjacentTo(tf2));
- assertTrue(tf2.isAdjacentTo(tf1));
- assertFalse(tf1.isAdjacentTo(tf0));
- assertFalse(tf0.isAdjacentTo(tf1));
- assertFalse(tf0.isAdjacentTo(tf0));
- assertFalse(tf1.isAdjacentTo(tf1));
+ assertNull(task0.getAdjacentTaskFragments());
+ assertSame(adjTasks, task1.getAdjacentTaskFragments());
+ assertSame(adjTasks, task2.getAdjacentTaskFragments());
+ assertFalse(adjTasks.contains(task0));
+ assertTrue(task1.isAdjacentTo(task2));
+ assertTrue(task2.isAdjacentTo(task1));
+ assertFalse(task1.isAdjacentTo(task0));
+ assertFalse(task0.isAdjacentTo(task1));
+ assertFalse(task0.isAdjacentTo(task0));
+ assertFalse(task1.isAdjacentTo(task1));
}
@EnableFlags(Flags.FLAG_ALLOW_MULTIPLE_ADJACENT_TASK_FRAGMENTS)
@Test
public void testRemoveFromAdjacentTaskFragmentsWhenRemove() {
- final Task task = createTask(mDisplayContent);
- final TaskFragment tf0 = createTaskFragmentWithActivity(task);
- final TaskFragment tf1 = createTaskFragmentWithActivity(task);
- final TaskFragment tf2 = createTaskFragmentWithActivity(task);
- final TaskFragment.AdjacentSet adjacentTfs = new TaskFragment.AdjacentSet(tf0, tf1, tf2);
- tf0.setAdjacentTaskFragments(adjacentTfs);
+ final Task task0 = createTask(mDisplayContent);
+ final Task task1 = createTask(mDisplayContent);
+ final Task task2 = createTask(mDisplayContent);
+ final TaskFragment.AdjacentSet adjTasks = new TaskFragment.AdjacentSet(task0, task1, task2);
+ task0.setAdjacentTaskFragments(adjTasks);
- tf0.removeImmediately();
+ task0.removeImmediately();
- assertNull(tf0.getAdjacentTaskFragments());
- assertSame(adjacentTfs, tf1.getAdjacentTaskFragments());
- assertSame(adjacentTfs, tf2.getAdjacentTaskFragments());
- assertFalse(adjacentTfs.contains(tf0));
+ assertNull(task0.getAdjacentTaskFragments());
+ assertSame(adjTasks, task1.getAdjacentTaskFragments());
+ assertSame(adjTasks, task2.getAdjacentTaskFragments());
+ assertFalse(adjTasks.contains(task0));
}
private WindowState createAppWindow(ActivityRecord app, String name) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index 78f32c1..2ee34d3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -433,8 +433,8 @@
final WallpaperWindowToken wallpaperWindowToken = spy(new WallpaperWindowToken(mWm,
mock(IBinder.class), true, mDisplayContent, true /* ownerCanManageAppTokens */));
- final WindowState wallpaperWindow = createWindow(null, TYPE_WALLPAPER, wallpaperWindowToken,
- "wallpaperWindow");
+ final WindowState wallpaperWindow = newWindowBuilder("wallpaperWindow",
+ TYPE_WALLPAPER).setWindowToken(wallpaperWindowToken).build();
wallpaperWindowToken.setVisibleRequested(false);
transition.collect(wallpaperWindowToken);
wallpaperWindowToken.setVisibleRequested(true);
@@ -630,8 +630,8 @@
// Make DA organized so we can check that they don't get included.
WindowContainer parent = wallpaperWindowToken.getParent();
makeDisplayAreaOrganized(parent, mDisplayContent);
- final WindowState wallpaperWindow = createWindow(null, TYPE_WALLPAPER, wallpaperWindowToken,
- "wallpaperWindow");
+ final WindowState wallpaperWindow = newWindowBuilder("wallpaperWindow",
+ TYPE_WALLPAPER).setWindowToken(wallpaperWindowToken).build();
wallpaperWindowToken.setVisibleRequested(false);
transition.collect(wallpaperWindowToken);
wallpaperWindowToken.setVisibleRequested(true);
@@ -1114,15 +1114,15 @@
// Simulate gesture navigation (non-movable) so it is not seamless.
doReturn(false).when(displayPolicy).navigationBarCanMove();
final Task task = createActivityRecord(mDisplayContent).getTask();
- final WindowState statusBar = createWindow(null, TYPE_STATUS_BAR, "statusBar");
- final WindowState navBar = createWindow(null, TYPE_NAVIGATION_BAR, "navBar");
- final WindowState ime = createWindow(null, TYPE_INPUT_METHOD, "ime");
+ final WindowState statusBar = newWindowBuilder("statusBar", TYPE_STATUS_BAR).build();
+ final WindowState navBar = newWindowBuilder("navBar", TYPE_NAVIGATION_BAR).build();
+ final WindowState ime = newWindowBuilder("ime", TYPE_INPUT_METHOD).build();
final WindowToken decorToken = new WindowToken.Builder(mWm, mock(IBinder.class),
TYPE_NAVIGATION_BAR_PANEL).setDisplayContent(mDisplayContent)
.setRoundedCornerOverlay(true).build();
- final WindowState screenDecor =
- createWindow(null, decorToken.windowType, decorToken, "screenDecor");
- final WindowState[] windows = { statusBar, navBar, ime, screenDecor };
+ final WindowState screenDecor = newWindowBuilder("screenDecor",
+ decorToken.windowType).setWindowToken(decorToken).build();
+ final WindowState[] windows = {statusBar, navBar, ime, screenDecor};
makeWindowVisible(windows);
mDisplayContent.getDisplayPolicy().addWindowLw(statusBar, statusBar.mAttrs);
mDisplayContent.getDisplayPolicy().addWindowLw(navBar, navBar.mAttrs);
@@ -1191,7 +1191,7 @@
}
private void testShellRotationOpen(TestTransitionPlayer player) {
- final WindowState statusBar = createWindow(null, TYPE_STATUS_BAR, "statusBar");
+ final WindowState statusBar = newWindowBuilder("statusBar", TYPE_STATUS_BAR).build();
makeWindowVisible(statusBar);
mDisplayContent.getDisplayPolicy().addWindowLw(statusBar, statusBar.mAttrs);
final ActivityRecord app = createActivityRecord(mDisplayContent);
@@ -1239,7 +1239,7 @@
}
private void testFixedRotationOpen(TestTransitionPlayer player) {
- final WindowState statusBar = createWindow(null, TYPE_STATUS_BAR, "statusBar");
+ final WindowState statusBar = newWindowBuilder("statusBar", TYPE_STATUS_BAR).build();
makeWindowVisible(statusBar);
mDisplayContent.getDisplayPolicy().addWindowLw(statusBar, statusBar.mAttrs);
final WindowState navBar = createNavBarWithProvidedInsets(mDisplayContent);
@@ -2272,15 +2272,24 @@
public void cleanUp(SurfaceControl.Transaction t) {
}
});
+ assertEquals(WindowAnimator.PENDING_STATE_NONE, mWm.mAnimator.mPendingState);
+ app.startAnimation(app.getPendingTransaction(), mock(AnimationAdapter.class),
+ false /* hidden */, SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION);
+ assertEquals(WindowAnimator.PENDING_STATE_HAS_CHANGES, mWm.mAnimator.mPendingState);
+
final Task task = app.getTask();
transition.collect(task);
+ assertEquals(WindowAnimator.PENDING_STATE_NEED_APPLY, mWm.mAnimator.mPendingState);
final Rect bounds = new Rect(task.getBounds());
Configuration c = new Configuration(task.getRequestedOverrideConfiguration());
bounds.inset(10, 10);
c.windowConfiguration.setBounds(bounds);
task.onRequestedOverrideConfigurationChanged(c);
assertTrue(freezeCalls.contains(task));
- transition.abort();
+
+ transition.start();
+ mWm.mSyncEngine.abort(transition.getSyncId());
+ assertEquals(WindowAnimator.PENDING_STATE_NONE, mWm.mAnimator.mPendingState);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
index eb89a9f..358448e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
@@ -243,8 +243,8 @@
final WindowState homeWindow = createWallpaperTargetWindow(dc);
- WindowState otherWindow = createWindow(null /* parent */, TYPE_APPLICATION, dc,
- "otherWindow");
+ WindowState otherWindow = newWindowBuilder("otherWindow", TYPE_APPLICATION).setDisplay(
+ dc).build();
dc.mWallpaperController.adjustWallpaperWindows();
@@ -275,7 +275,7 @@
public void testUpdateWallpaperTarget() {
final DisplayContent dc = mDisplayContent;
final WindowState homeWin = createWallpaperTargetWindow(dc);
- final WindowState appWin = createWindow(null, TYPE_BASE_APPLICATION, "app");
+ final WindowState appWin = newWindowBuilder("app", TYPE_BASE_APPLICATION).build();
appWin.mAttrs.flags |= FLAG_SHOW_WALLPAPER;
makeWindowVisible(appWin);
@@ -290,9 +290,9 @@
public void testShowWhenLockedWallpaperTarget() {
final WindowState wallpaperWindow = createWallpaperWindow(mDisplayContent);
wallpaperWindow.mToken.asWallpaperToken().setShowWhenLocked(true);
- final WindowState behind = createWindow(null, TYPE_BASE_APPLICATION, "behind");
- final WindowState topTranslucent = createWindow(null, TYPE_BASE_APPLICATION,
- "topTranslucent");
+ final WindowState behind = newWindowBuilder("behind", TYPE_BASE_APPLICATION).build();
+ final WindowState topTranslucent = newWindowBuilder("topTranslucent",
+ TYPE_BASE_APPLICATION).build();
behind.mAttrs.width = behind.mAttrs.height = topTranslucent.mAttrs.width =
topTranslucent.mAttrs.height = WindowManager.LayoutParams.MATCH_PARENT;
topTranslucent.mAttrs.flags |= WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
@@ -314,8 +314,8 @@
// Only transient-launch transition will make notification shade as last resort target.
// This verifies that regular transition won't choose invisible keyguard as the target.
- final WindowState keyguard = createWindow(null /* parent */,
- WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE, "keyguard");
+ final WindowState keyguard = newWindowBuilder("keyguard",
+ WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE).build();
keyguard.mAttrs.flags |= FLAG_SHOW_WALLPAPER;
registerTestTransitionPlayer();
final Transition transition = wallpaperWindow.mTransitionController.createTransition(
@@ -568,8 +568,8 @@
private WindowState createWallpaperWindow(DisplayContent dc) {
final WindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm, mock(IBinder.class),
true /* explicit */, dc, true /* ownerCanManageAppTokens */);
- return createWindow(null /* parent */, TYPE_WALLPAPER, wallpaperWindowToken,
- "wallpaperWindow");
+ return newWindowBuilder("wallpaperWindow", TYPE_WALLPAPER).setWindowToken(
+ wallpaperWindowToken).build();
}
private WindowState createWallpaperTargetWindow(DisplayContent dc) {
@@ -578,8 +578,8 @@
.build();
homeActivity.setVisibility(true);
- WindowState appWindow = createWindow(null /* parent */, TYPE_BASE_APPLICATION,
- homeActivity, "wallpaperTargetWindow");
+ WindowState appWindow = newWindowBuilder("wallpaperTargetWindow",
+ TYPE_BASE_APPLICATION).setWindowToken(homeActivity).build();
appWindow.getAttrs().flags |= FLAG_SHOW_WALLPAPER;
appWindow.mHasSurface = true;
spyOn(appWindow);