Merge "Set compile_dex explicitly for FakeApexSystemServices" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index e71ded9..8ced596 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -101,6 +101,7 @@
"framework-jobscheduler-job.flags-aconfig-java",
"framework_graphics_flags_java_lib",
"hwui_flags_java_lib",
+ "keystore2_flags_java-framework",
"power_flags_lib",
"sdk_sandbox_flags_lib",
"surfaceflinger_flags_java_lib",
@@ -374,6 +375,7 @@
min_sdk_version: "30",
apex_available: [
"//apex_available:platform",
+ "com.android.btservices",
"com.android.mediaprovider",
"com.android.permission",
],
diff --git a/Android.bp b/Android.bp
index d2e8003..dc48668 100644
--- a/Android.bp
+++ b/Android.bp
@@ -105,7 +105,6 @@
":android.hardware.radio.data-V3-java-source",
":android.hardware.radio.network-V3-java-source",
":android.hardware.radio.voice-V3-java-source",
- ":android.hardware.security.keymint-V3-java-source",
":android.hardware.security.secureclock-V1-java-source",
":android.hardware.thermal-V2-java-source",
":android.hardware.tv.tuner-V2-java-source",
@@ -114,7 +113,6 @@
":android.security.legacykeystore-java-source",
":android.security.maintenance-java-source",
":android.security.metrics-java-source",
- ":android.system.keystore2-V4-java-source",
":android.hardware.cas-V1-java-source",
":credstore_aidl",
":dumpstate_aidl",
@@ -149,7 +147,16 @@
":framework-javastream-protos",
":statslog-framework-java-gen", // FrameworkStatsLog.java
":audio_policy_configuration_V7_0",
- ],
+ ] + select(release_flag("RELEASE_ATTEST_MODULES"), {
+ true: [
+ ":android.hardware.security.keymint-V4-java-source",
+ ":android.system.keystore2-V5-java-source",
+ ],
+ default: [
+ ":android.hardware.security.keymint-V3-java-source",
+ ":android.system.keystore2-V4-java-source",
+ ],
+ }),
}
java_library {
diff --git a/FF_LEADS_OWNERS b/FF_LEADS_OWNERS
new file mode 100644
index 0000000..a650c6b
--- /dev/null
+++ b/FF_LEADS_OWNERS
@@ -0,0 +1,10 @@
+bills@google.com
+carmenjackson@google.com
+nalini@google.com
+nosh@google.com
+olilan@google.com
+philipcuadra@google.com
+rajekumar@google.com
+shayba@google.com
+timmurray@google.com
+zezeozue@google.com
diff --git a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java
index f726361..a88796c3 100644
--- a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java
+++ b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java
@@ -217,6 +217,9 @@
serializer.attribute("", "selected", Boolean.toString(node.isSelected()));
serializer.attribute("", "bounds", AccessibilityNodeInfoHelper.getVisibleBoundsInScreen(
node, width, height).toShortString());
+ serializer.attribute("", "drawing-order", Integer.toString(node.getDrawingOrder()));
+ serializer.attribute("", "hint", safeCharSeqToString(node.getHintText()));
+
int count = node.getChildCount();
for (int i = 0; i < count; i++) {
AccessibilityNodeInfo child = node.getChild(i);
diff --git a/core/api/current.txt b/core/api/current.txt
index d2e89b4..1f45289 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -33882,9 +33882,12 @@
public class RemoteCallbackList<E extends android.os.IInterface> {
ctor public RemoteCallbackList();
method public int beginBroadcast();
+ method @FlaggedApi("android.os.binder_frozen_state_change_callback") public void broadcast(@NonNull java.util.function.Consumer<E>);
method public void finishBroadcast();
method public Object getBroadcastCookie(int);
method public E getBroadcastItem(int);
+ method @FlaggedApi("android.os.binder_frozen_state_change_callback") public int getFrozenCalleePolicy();
+ method @FlaggedApi("android.os.binder_frozen_state_change_callback") public int getMaxQueueSize();
method public Object getRegisteredCallbackCookie(int);
method public int getRegisteredCallbackCount();
method public E getRegisteredCallbackItem(int);
@@ -33894,6 +33897,21 @@
method public boolean register(E);
method public boolean register(E, Object);
method public boolean unregister(E);
+ field @FlaggedApi("android.os.binder_frozen_state_change_callback") public static final int FROZEN_CALLEE_POLICY_DROP = 3; // 0x3
+ field @FlaggedApi("android.os.binder_frozen_state_change_callback") public static final int FROZEN_CALLEE_POLICY_ENQUEUE_ALL = 1; // 0x1
+ field @FlaggedApi("android.os.binder_frozen_state_change_callback") public static final int FROZEN_CALLEE_POLICY_ENQUEUE_MOST_RECENT = 2; // 0x2
+ field @FlaggedApi("android.os.binder_frozen_state_change_callback") public static final int FROZEN_CALLEE_POLICY_UNSET = 0; // 0x0
+ }
+
+ @FlaggedApi("android.os.binder_frozen_state_change_callback") public static final class RemoteCallbackList.Builder<E extends android.os.IInterface> {
+ ctor public RemoteCallbackList.Builder(int);
+ method @NonNull public android.os.RemoteCallbackList<E> build();
+ method @NonNull public android.os.RemoteCallbackList.Builder setInterfaceDiedCallback(@NonNull android.os.RemoteCallbackList.Builder.InterfaceDiedCallback<E>);
+ method @NonNull public android.os.RemoteCallbackList.Builder setMaxQueueSize(int);
+ }
+
+ @FlaggedApi("android.os.binder_frozen_state_change_callback") public static interface RemoteCallbackList.Builder.InterfaceDiedCallback<E extends android.os.IInterface> {
+ method public void onInterfaceDied(@NonNull android.os.RemoteCallbackList<E>, E, @Nullable Object);
}
public class RemoteException extends android.util.AndroidException {
@@ -40017,8 +40035,10 @@
method @NonNull public java.util.List<java.security.cert.X509Certificate> getGrantedCertificateChainFromId(long) throws android.security.keystore.KeyPermanentlyInvalidatedException, java.security.UnrecoverableKeyException;
method @NonNull public java.security.Key getGrantedKeyFromId(long) throws android.security.keystore.KeyPermanentlyInvalidatedException, java.security.UnrecoverableKeyException;
method @NonNull public java.security.KeyPair getGrantedKeyPairFromId(long) throws android.security.keystore.KeyPermanentlyInvalidatedException, java.security.UnrecoverableKeyException;
+ method @FlaggedApi("android.security.keystore2.attest_modules") @NonNull public byte[] getSupplementaryAttestationInfo(int) throws android.security.KeyStoreException;
method public long grantKeyAccess(@NonNull String, int) throws android.security.KeyStoreException, java.security.UnrecoverableKeyException;
method public void revokeKeyAccess(@NonNull String, int) throws android.security.KeyStoreException, java.security.UnrecoverableKeyException;
+ field public static final int MODULE_HASH = -1879047468; // 0x900002d4
}
public class SecureKeyImportUnavailableException extends java.security.ProviderException {
@@ -45982,6 +46002,8 @@
method public long getDataUsageBytes();
method public long getDataUsageTime();
method @NonNull public int[] getNetworkTypes();
+ method @FlaggedApi("com.android.internal.telephony.flags.subscription_plan_allow_status_and_end_date") @Nullable public java.time.ZonedDateTime getPlanEndDate();
+ method @FlaggedApi("com.android.internal.telephony.flags.subscription_plan_allow_status_and_end_date") public int getSubscriptionStatus();
method @Nullable public CharSequence getSummary();
method @Nullable public CharSequence getTitle();
method public void writeToParcel(android.os.Parcel, int);
@@ -45992,6 +46014,11 @@
field public static final int LIMIT_BEHAVIOR_DISABLED = 0; // 0x0
field public static final int LIMIT_BEHAVIOR_THROTTLED = 2; // 0x2
field public static final int LIMIT_BEHAVIOR_UNKNOWN = -1; // 0xffffffff
+ field @FlaggedApi("com.android.internal.telephony.flags.subscription_plan_allow_status_and_end_date") public static final int SUBSCRIPTION_STATUS_ACTIVE = 1; // 0x1
+ field @FlaggedApi("com.android.internal.telephony.flags.subscription_plan_allow_status_and_end_date") public static final int SUBSCRIPTION_STATUS_INACTIVE = 2; // 0x2
+ field @FlaggedApi("com.android.internal.telephony.flags.subscription_plan_allow_status_and_end_date") public static final int SUBSCRIPTION_STATUS_SUSPENDED = 4; // 0x4
+ field @FlaggedApi("com.android.internal.telephony.flags.subscription_plan_allow_status_and_end_date") public static final int SUBSCRIPTION_STATUS_TRIAL = 3; // 0x3
+ field @FlaggedApi("com.android.internal.telephony.flags.subscription_plan_allow_status_and_end_date") public static final int SUBSCRIPTION_STATUS_UNKNOWN = 0; // 0x0
field public static final long TIME_UNKNOWN = -1L; // 0xffffffffffffffffL
}
@@ -46003,6 +46030,7 @@
method public android.telephony.SubscriptionPlan.Builder setDataLimit(long, int);
method public android.telephony.SubscriptionPlan.Builder setDataUsage(long, long);
method @NonNull public android.telephony.SubscriptionPlan.Builder setNetworkTypes(@NonNull int[]);
+ method @FlaggedApi("com.android.internal.telephony.flags.subscription_plan_allow_status_and_end_date") @NonNull public android.telephony.SubscriptionPlan.Builder setSubscriptionStatus(int);
method public android.telephony.SubscriptionPlan.Builder setSummary(@Nullable CharSequence);
method public android.telephony.SubscriptionPlan.Builder setTitle(@Nullable CharSequence);
}
diff --git a/core/api/lint-baseline.txt b/core/api/lint-baseline.txt
index e6a7ca5..a8e5e20 100644
--- a/core/api/lint-baseline.txt
+++ b/core/api/lint-baseline.txt
@@ -383,6 +383,10 @@
Method javax.microedition.khronos.egl.EGL10.eglCreatePixmapSurface(javax.microedition.khronos.egl.EGLDisplay, javax.microedition.khronos.egl.EGLConfig, Object, int[]): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+ExecutorRegistration: android.os.IBinder#addFrozenStateChangeCallback(android.os.IBinder.FrozenStateChangeCallback):
+ Registration methods should have overload that accepts delivery Executor: `addFrozenStateChangeCallback`
+
+
InvalidNullabilityOverride: android.app.Notification.TvExtender#extend(android.app.Notification.Builder) parameter #0:
Invalid nullability on parameter `builder` in method `extend`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
InvalidNullabilityOverride: android.media.midi.MidiUmpDeviceService#onBind(android.content.Intent) parameter #0:
@@ -395,6 +399,10 @@
Method can be invoked with an indexing operator from Kotlin: `set` (this is usually desirable; just make sure it makes sense for this type of object)
+MissingGetterMatchingBuilder: android.os.RemoteCallbackList.Builder#setInterfaceDiedCallback(android.os.RemoteCallbackList.Builder.InterfaceDiedCallback<E>):
+ android.os.RemoteCallbackList does not declare a `getInterfaceDiedCallback()` method matching method android.os.RemoteCallbackList.Builder.setInterfaceDiedCallback(android.os.RemoteCallbackList.Builder.InterfaceDiedCallback<E>)
+
+
RequiresPermission: android.accounts.AccountManager#getAccountsByTypeAndFeatures(String, String[], android.accounts.AccountManagerCallback<android.accounts.Account[]>, android.os.Handler):
Method 'getAccountsByTypeAndFeatures' documentation mentions permissions without declaring @RequiresPermission
RequiresPermission: android.accounts.AccountManager#hasFeatures(android.accounts.Account, String[], android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler):
@@ -1445,7 +1453,6 @@
New API must be flagged with @FlaggedApi: method android.graphics.text.PositionedGlyphs.getItalicOverride(int)
UnflaggedApi: android.graphics.text.PositionedGlyphs#getWeightOverride(int):
New API must be flagged with @FlaggedApi: method android.graphics.text.PositionedGlyphs.getWeightOverride(int)
-
UnflaggedApi: android.media.MediaRoute2Info#TYPE_REMOTE_CAR:
New API must be flagged with @FlaggedApi: field android.media.MediaRoute2Info.TYPE_REMOTE_CAR
UnflaggedApi: android.media.MediaRoute2Info#TYPE_REMOTE_COMPUTER:
diff --git a/core/java/android/app/AppCompatCallbacks.java b/core/java/android/app/AppCompatCallbacks.java
index f2debfc..4ef8eb7 100644
--- a/core/java/android/app/AppCompatCallbacks.java
+++ b/core/java/android/app/AppCompatCallbacks.java
@@ -28,6 +28,7 @@
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class AppCompatCallbacks implements Compatibility.BehaviorChangeDelegate {
private final long[] mDisabledChanges;
private final long[] mLoggableChanges;
diff --git a/core/java/android/app/compat/ChangeIdStateCache.java b/core/java/android/app/compat/ChangeIdStateCache.java
index dea4e9c8..a185d6a 100644
--- a/core/java/android/app/compat/ChangeIdStateCache.java
+++ b/core/java/android/app/compat/ChangeIdStateCache.java
@@ -29,13 +29,24 @@
* Handles caching of calls to {@link com.android.internal.compat.IPlatformCompat}
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class ChangeIdStateCache
extends PropertyInvalidatedCache<ChangeIdStateQuery, Boolean> {
private static final String CACHE_KEY = "cache_key.is_compat_change_enabled";
private static final int MAX_ENTRIES = 64;
- private static boolean sDisabled = false;
+ private static boolean sDisabled = getDefaultDisabled();
private volatile IPlatformCompat mPlatformCompat;
+
+ @android.ravenwood.annotation.RavenwoodReplace
+ private static boolean getDefaultDisabled() {
+ return false;
+ }
+
+ private static boolean getDefaultDisabled$ravenwood() {
+ return true; // TODO(b/376676753) Disable the cache for now.
+ }
+
/** @hide */
public ChangeIdStateCache() {
super(MAX_ENTRIES, CACHE_KEY);
diff --git a/core/java/android/app/compat/ChangeIdStateQuery.java b/core/java/android/app/compat/ChangeIdStateQuery.java
index 7598d6c..26d9ab6 100644
--- a/core/java/android/app/compat/ChangeIdStateQuery.java
+++ b/core/java/android/app/compat/ChangeIdStateQuery.java
@@ -35,6 +35,7 @@
* @hide
*/
@Immutable
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
final class ChangeIdStateQuery {
static final int QUERY_BY_PACKAGE_NAME = 0;
diff --git a/core/java/android/app/compat/CompatChanges.java b/core/java/android/app/compat/CompatChanges.java
index d7b2ab4..643d4c9 100644
--- a/core/java/android/app/compat/CompatChanges.java
+++ b/core/java/android/app/compat/CompatChanges.java
@@ -39,6 +39,7 @@
* @hide
*/
@SystemApi
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class CompatChanges {
private static final ChangeIdStateCache QUERY_CACHE = new ChangeIdStateCache();
diff --git a/core/java/android/app/compat/PackageOverride.java b/core/java/android/app/compat/PackageOverride.java
index ebc2945..ffc1eec 100644
--- a/core/java/android/app/compat/PackageOverride.java
+++ b/core/java/android/app/compat/PackageOverride.java
@@ -36,6 +36,7 @@
* @hide
*/
@SystemApi
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class PackageOverride {
/** @hide */
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 8de86d5..2df187b 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -292,6 +292,10 @@
* <p>
* The value of a property will only have a single type, as defined by
* the property itself.
+ *
+ * <p class="note"><strong>Note:</strong>
+ * In android version {@link Build.VERSION_CODES#VANILLA_ICE_CREAM} and earlier,
+ * the {@code equals} and {@code hashCode} methods for this class may not function as expected.
*/
public static final class Property implements Parcelable {
private static final int TYPE_BOOLEAN = 1;
@@ -523,6 +527,40 @@
return new Property[size];
}
};
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof Property)) {
+ return false;
+ }
+ final Property property = (Property) obj;
+ return mType == property.mType &&
+ Objects.equals(mName, property.mName) &&
+ Objects.equals(mClassName, property.mClassName) &&
+ Objects.equals(mPackageName, property.mPackageName) &&
+ (mType == TYPE_BOOLEAN ? mBooleanValue == property.mBooleanValue :
+ mType == TYPE_FLOAT ? Float.compare(mFloatValue, property.mFloatValue) == 0 :
+ mType == TYPE_INTEGER ? mIntegerValue == property.mIntegerValue :
+ mType == TYPE_RESOURCE ? mIntegerValue == property.mIntegerValue :
+ mStringValue.equals(property.mStringValue));
+ }
+
+ @Override
+ public int hashCode() {
+ int result = Objects.hash(mName, mType, mClassName, mPackageName);
+ if (mType == TYPE_BOOLEAN) {
+ result = 31 * result + (mBooleanValue ? 1 : 0);
+ } else if (mType == TYPE_FLOAT) {
+ result = 31 * result + Float.floatToIntBits(mFloatValue);
+ } else if (mType == TYPE_INTEGER) {
+ result = 31 * result + mIntegerValue;
+ } else if (mType == TYPE_RESOURCE) {
+ result = 31 * result + mIntegerValue;
+ } else if (mType == TYPE_STRING) {
+ result = 31 * result + mStringValue.hashCode();
+ }
+ return result;
+ }
}
/**
diff --git a/core/java/android/hardware/contexthub/OWNERS b/core/java/android/hardware/contexthub/OWNERS
new file mode 100644
index 0000000..a65a2bf
--- /dev/null
+++ b/core/java/android/hardware/contexthub/OWNERS
@@ -0,0 +1,2 @@
+# ContextHub team
+file:platform/system/chre:/OWNERS
diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS
index 24e1d66..e63b664 100644
--- a/core/java/android/os/OWNERS
+++ b/core/java/android/os/OWNERS
@@ -128,3 +128,6 @@
# Dropbox
per-file DropBoxManager* = mwachens@google.com
+
+# Flags
+per-file flags.aconfig = file:/FF_LEADS_OWNERS
diff --git a/core/java/android/os/RemoteCallbackList.java b/core/java/android/os/RemoteCallbackList.java
index 2cb86f7..91c482fa 100644
--- a/core/java/android/os/RemoteCallbackList.java
+++ b/core/java/android/os/RemoteCallbackList.java
@@ -16,11 +16,19 @@
package android.os;
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.util.ArrayMap;
import android.util.Slog;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
@@ -30,7 +38,7 @@
* {@link android.app.Service} to its clients. In particular, this:
*
* <ul>
- * <li> Keeps track of a set of registered {@link IInterface} callbacks,
+ * <li> Keeps track of a set of registered {@link IInterface} objects,
* taking care to identify them through their underlying unique {@link IBinder}
* (by calling {@link IInterface#asBinder IInterface.asBinder()}.
* <li> Attaches a {@link IBinder.DeathRecipient IBinder.DeathRecipient} to
@@ -47,7 +55,7 @@
* the registered clients, use {@link #beginBroadcast},
* {@link #getBroadcastItem}, and {@link #finishBroadcast}.
*
- * <p>If a registered callback's process goes away, this class will take
+ * <p>If a registered interface's process goes away, this class will take
* care of automatically removing it from the list. If you want to do
* additional work in this situation, you can create a subclass that
* implements the {@link #onCallbackDied} method.
@@ -56,78 +64,358 @@
public class RemoteCallbackList<E extends IInterface> {
private static final String TAG = "RemoteCallbackList";
+ private static final int DEFAULT_MAX_QUEUE_SIZE = 1000;
+
+
+ /**
+ * @hide
+ */
+ @IntDef(prefix = {"FROZEN_CALLEE_POLICY_"}, value = {
+ FROZEN_CALLEE_POLICY_UNSET,
+ FROZEN_CALLEE_POLICY_ENQUEUE_ALL,
+ FROZEN_CALLEE_POLICY_ENQUEUE_MOST_RECENT,
+ FROZEN_CALLEE_POLICY_DROP,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface FrozenCalleePolicy {
+ }
+
+ /**
+ * Callbacks are invoked immediately regardless of the frozen state of the target process.
+ *
+ * Not recommended. Only exists for backward-compatibility. This represents the behavior up to
+ * SDK 35. Starting with SDK 36, clients should set a policy to govern callback invocations when
+ * recipients are frozen.
+ */
+ @FlaggedApi(Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK)
+ public static final int FROZEN_CALLEE_POLICY_UNSET = 0;
+
+ /**
+ * When the callback recipient's process is frozen, callbacks are enqueued so they're invoked
+ * after the recipient is unfrozen.
+ *
+ * This is commonly used when the recipient wants to receive all callbacks without losing any
+ * history, e.g. the recipient maintains a running count of events that occurred.
+ *
+ * Queued callbacks are invoked in the order they were originally broadcasted.
+ */
+ @FlaggedApi(Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK)
+ public static final int FROZEN_CALLEE_POLICY_ENQUEUE_ALL = 1;
+
+ /**
+ * When the callback recipient's process is frozen, only the most recent callback is enqueued,
+ * which is later invoked after the recipient is unfrozen.
+ *
+ * This can be used when only the most recent state matters, for instance when clients are
+ * listening to screen brightness changes.
+ */
+ @FlaggedApi(Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK)
+ public static final int FROZEN_CALLEE_POLICY_ENQUEUE_MOST_RECENT = 2;
+
+ /**
+ * When the callback recipient's process is frozen, callbacks are suppressed as if they never
+ * happened.
+ *
+ * This could be useful in the case where the recipient wishes to react to callbacks only when
+ * they occur while the recipient is not frozen. For example, certain network events are only
+ * worth responding to if the response can be immediate. Another example is recipients having
+ * another way of getting the latest state once it's unfrozen. Therefore there is no need to
+ * save callbacks that happened while the recipient was frozen.
+ */
+ @FlaggedApi(Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK)
+ public static final int FROZEN_CALLEE_POLICY_DROP = 3;
+
@UnsupportedAppUsage
- /*package*/ ArrayMap<IBinder, Callback> mCallbacks
- = new ArrayMap<IBinder, Callback>();
+ /*package*/ ArrayMap<IBinder, Interface> mInterfaces = new ArrayMap<IBinder, Interface>();
private Object[] mActiveBroadcast;
private int mBroadcastCount = -1;
private boolean mKilled = false;
private StringBuilder mRecentCallers;
- private final class Callback implements IBinder.DeathRecipient {
- final E mCallback;
- final Object mCookie;
+ private final @FrozenCalleePolicy int mFrozenCalleePolicy;
+ private final int mMaxQueueSize;
- Callback(E callback, Object cookie) {
- mCallback = callback;
+ private final class Interface implements IBinder.DeathRecipient,
+ IBinder.FrozenStateChangeCallback {
+ final IBinder mBinder;
+ final E mInterface;
+ final Object mCookie;
+ final Queue<Consumer<E>> mCallbackQueue;
+ int mCurrentState = IBinder.FrozenStateChangeCallback.STATE_UNFROZEN;
+
+ Interface(E callbackInterface, Object cookie) {
+ mBinder = callbackInterface.asBinder();
+ mInterface = callbackInterface;
mCookie = cookie;
+ mCallbackQueue = mFrozenCalleePolicy == FROZEN_CALLEE_POLICY_ENQUEUE_ALL
+ || mFrozenCalleePolicy == FROZEN_CALLEE_POLICY_ENQUEUE_MOST_RECENT
+ ? new ConcurrentLinkedQueue<>() : null;
+ }
+
+ @Override
+ public synchronized void onFrozenStateChanged(@NonNull IBinder who, int state) {
+ if (state == STATE_UNFROZEN && mCallbackQueue != null) {
+ while (!mCallbackQueue.isEmpty()) {
+ Consumer<E> callback = mCallbackQueue.poll();
+ callback.accept(mInterface);
+ }
+ }
+ mCurrentState = state;
+ }
+
+ void addCallback(@NonNull Consumer<E> callback) {
+ if (mFrozenCalleePolicy == FROZEN_CALLEE_POLICY_UNSET) {
+ callback.accept(mInterface);
+ return;
+ }
+ synchronized (this) {
+ if (mCurrentState == STATE_UNFROZEN) {
+ callback.accept(mInterface);
+ return;
+ }
+ switch (mFrozenCalleePolicy) {
+ case FROZEN_CALLEE_POLICY_ENQUEUE_ALL:
+ if (mCallbackQueue.size() >= mMaxQueueSize) {
+ mCallbackQueue.poll();
+ }
+ mCallbackQueue.offer(callback);
+ break;
+ case FROZEN_CALLEE_POLICY_ENQUEUE_MOST_RECENT:
+ mCallbackQueue.clear();
+ mCallbackQueue.offer(callback);
+ break;
+ case FROZEN_CALLEE_POLICY_DROP:
+ // Do nothing. Just ignore the callback.
+ break;
+ case FROZEN_CALLEE_POLICY_UNSET:
+ // Do nothing. Should have returned at the start of the method.
+ break;
+ }
+ }
+ }
+
+ void maybeSubscribeToFrozenCallback() throws RemoteException {
+ if (mFrozenCalleePolicy != FROZEN_CALLEE_POLICY_UNSET) {
+ try {
+ mBinder.addFrozenStateChangeCallback(this);
+ } catch (UnsupportedOperationException e) {
+ // The kernel does not support frozen notifications. In this case we want to
+ // silently fall back to FROZEN_CALLEE_POLICY_UNSET. This is done by simply
+ // ignoring the error and moving on. mCurrentState would always be
+ // STATE_UNFROZEN and all callbacks are invoked immediately.
+ }
+ }
+ }
+
+ void maybeUnsubscribeFromFrozenCallback() {
+ if (mFrozenCalleePolicy != FROZEN_CALLEE_POLICY_UNSET) {
+ try {
+ mBinder.removeFrozenStateChangeCallback(this);
+ } catch (UnsupportedOperationException e) {
+ // The kernel does not support frozen notifications. Ignore the error and move
+ // on.
+ }
+ }
}
public void binderDied() {
- synchronized (mCallbacks) {
- mCallbacks.remove(mCallback.asBinder());
+ synchronized (mInterfaces) {
+ mInterfaces.remove(mBinder);
+ maybeUnsubscribeFromFrozenCallback();
}
- onCallbackDied(mCallback, mCookie);
+ onCallbackDied(mInterface, mCookie);
}
}
/**
+ * Builder for {@link RemoteCallbackList}.
+ *
+ * @param <E> The remote callback interface type.
+ */
+ @FlaggedApi(Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK)
+ public static final class Builder<E extends IInterface> {
+ private @FrozenCalleePolicy int mFrozenCalleePolicy;
+ private int mMaxQueueSize = DEFAULT_MAX_QUEUE_SIZE;
+ private InterfaceDiedCallback mInterfaceDiedCallback;
+
+ /**
+ * Creates a Builder for {@link RemoteCallbackList}.
+ *
+ * @param frozenCalleePolicy When the callback recipient's process is frozen, this parameter
+ * specifies when/whether callbacks are invoked. It's important to choose a strategy that's
+ * right for the use case. Leaving the policy unset with {@link #FROZEN_CALLEE_POLICY_UNSET}
+ * is not recommended as it allows callbacks to be invoked while the recipient is frozen.
+ */
+ public Builder(@FrozenCalleePolicy int frozenCalleePolicy) {
+ mFrozenCalleePolicy = frozenCalleePolicy;
+ }
+
+ /**
+ * Sets the max queue size.
+ *
+ * @param maxQueueSize The max size limit on the queue that stores callbacks added when the
+ * recipient's process is frozen. Once the limit is reached, the oldest callback is dropped
+ * to keep the size under the limit. Should only be called for
+ * {@link #FROZEN_CALLEE_POLICY_ENQUEUE_ALL}.
+ *
+ * @return This builder.
+ * @throws IllegalArgumentException if the maxQueueSize is not positive.
+ * @throws UnsupportedOperationException if frozenCalleePolicy is not
+ * {@link #FROZEN_CALLEE_POLICY_ENQUEUE_ALL}.
+ */
+ public @NonNull Builder setMaxQueueSize(int maxQueueSize) {
+ if (maxQueueSize <= 0) {
+ throw new IllegalArgumentException("maxQueueSize must be positive");
+ }
+ if (mFrozenCalleePolicy != FROZEN_CALLEE_POLICY_ENQUEUE_ALL) {
+ throw new UnsupportedOperationException(
+ "setMaxQueueSize can only be called for FROZEN_CALLEE_POLICY_ENQUEUE_ALL");
+ }
+ mMaxQueueSize = maxQueueSize;
+ return this;
+ }
+
+ /**
+ * Sets the callback to be invoked when an interface dies.
+ */
+ public @NonNull Builder setInterfaceDiedCallback(
+ @NonNull InterfaceDiedCallback<E> callback) {
+ mInterfaceDiedCallback = callback;
+ return this;
+ }
+
+ /**
+ * For notifying when the process hosting a callback interface has died.
+ *
+ * @param <E> The remote callback interface type.
+ */
+ @FlaggedApi(Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK)
+ public interface InterfaceDiedCallback<E extends IInterface> {
+ /**
+ * Invoked when a callback interface has died.
+ *
+ * @param remoteCallbackList the list that the interface was registered with.
+ * @param deadInterface the interface that has died.
+ * @param cookie the cookie specified on interface registration.
+ */
+ void onInterfaceDied(@NonNull RemoteCallbackList<E> remoteCallbackList,
+ E deadInterface, @Nullable Object cookie);
+ }
+
+ /**
+ * Builds and returns a {@link RemoteCallbackList}.
+ *
+ * @return The built {@link RemoteCallbackList} object.
+ */
+ public @NonNull RemoteCallbackList<E> build() {
+ if (mInterfaceDiedCallback != null) {
+ return new RemoteCallbackList<E>(mFrozenCalleePolicy, mMaxQueueSize) {
+ @Override
+ public void onCallbackDied(E deadInterface, Object cookie) {
+ mInterfaceDiedCallback.onInterfaceDied(this, deadInterface, cookie);
+ }
+ };
+ }
+ return new RemoteCallbackList<E>(mFrozenCalleePolicy, mMaxQueueSize);
+ }
+ }
+
+ /**
+ * Returns the frozen callee policy.
+ *
+ * @return The frozen callee policy.
+ */
+ @FlaggedApi(Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK)
+ public @FrozenCalleePolicy int getFrozenCalleePolicy() {
+ return mFrozenCalleePolicy;
+ }
+
+ /**
+ * Returns the max queue size.
+ *
+ * @return The max queue size.
+ */
+ @FlaggedApi(Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK)
+ public int getMaxQueueSize() {
+ return mMaxQueueSize;
+ }
+
+ /**
+ * Creates a RemoteCallbackList with {@link #FROZEN_CALLEE_POLICY_UNSET}. This is equivalent to
+ * <pre>
+ * new RemoteCallbackList.Build(RemoteCallbackList.FROZEN_CALLEE_POLICY_UNSET).build()
+ * </pre>
+ */
+ public RemoteCallbackList() {
+ this(FROZEN_CALLEE_POLICY_UNSET, DEFAULT_MAX_QUEUE_SIZE);
+ }
+
+ /**
+ * Creates a RemoteCallbackList with the specified frozen callee policy.
+ *
+ * @param frozenCalleePolicy When the callback recipient's process is frozen, this parameter
+ * specifies when/whether callbacks are invoked. It's important to choose a strategy that's
+ * right for the use case. Leaving the policy unset with {@link #FROZEN_CALLEE_POLICY_UNSET}
+ * is not recommended as it allows callbacks to be invoked while the recipient is frozen.
+ *
+ * @param maxQueueSize The max size limit on the queue that stores callbacks added when the
+ * recipient's process is frozen. Once the limit is reached, the oldest callbacks would be
+ * dropped to keep the size under limit. Ignored except for
+ * {@link #FROZEN_CALLEE_POLICY_ENQUEUE_ALL}.
+ */
+ private RemoteCallbackList(@FrozenCalleePolicy int frozenCalleePolicy, int maxQueueSize) {
+ mFrozenCalleePolicy = frozenCalleePolicy;
+ mMaxQueueSize = maxQueueSize;
+ }
+
+ /**
* Simple version of {@link RemoteCallbackList#register(E, Object)}
* that does not take a cookie object.
*/
- public boolean register(E callback) {
- return register(callback, null);
+ public boolean register(E callbackInterface) {
+ return register(callbackInterface, null);
}
/**
- * Add a new callback to the list. This callback will remain in the list
+ * Add a new interface to the list. This interface will remain in the list
* until a corresponding call to {@link #unregister} or its hosting process
- * goes away. If the callback was already registered (determined by
- * checking to see if the {@link IInterface#asBinder callback.asBinder()}
- * object is already in the list), then it will be left as-is.
+ * goes away. If the interface was already registered (determined by
+ * checking to see if the {@link IInterface#asBinder callbackInterface.asBinder()}
+ * object is already in the list), then it will be replaced with the new interface.
* Registrations are not counted; a single call to {@link #unregister}
- * will remove a callback after any number calls to register it.
+ * will remove an interface after any number calls to register it.
*
- * @param callback The callback interface to be added to the list. Must
+ * @param callbackInterface The callback interface to be added to the list. Must
* not be null -- passing null here will cause a NullPointerException.
* Most services will want to check for null before calling this with
* an object given from a client, so that clients can't crash the
* service with bad data.
*
* @param cookie Optional additional data to be associated with this
- * callback.
- *
- * @return Returns true if the callback was successfully added to the list.
+ * interface.
+ *
+ * @return Returns true if the interface was successfully added to the list.
* Returns false if it was not added, either because {@link #kill} had
- * previously been called or the callback's process has gone away.
+ * previously been called or the interface's process has gone away.
*
* @see #unregister
* @see #kill
* @see #onCallbackDied
*/
- public boolean register(E callback, Object cookie) {
- synchronized (mCallbacks) {
+ public boolean register(E callbackInterface, Object cookie) {
+ synchronized (mInterfaces) {
if (mKilled) {
return false;
}
// Flag unusual case that could be caused by a leak. b/36778087
- logExcessiveCallbacks();
- IBinder binder = callback.asBinder();
+ logExcessiveInterfaces();
+ IBinder binder = callbackInterface.asBinder();
try {
- Callback cb = new Callback(callback, cookie);
- unregister(callback);
- binder.linkToDeath(cb, 0);
- mCallbacks.put(binder, cb);
+ Interface i = new Interface(callbackInterface, cookie);
+ unregister(callbackInterface);
+ binder.linkToDeath(i, 0);
+ i.maybeSubscribeToFrozenCallback();
+ mInterfaces.put(binder, i);
return true;
} catch (RemoteException e) {
return false;
@@ -136,27 +424,28 @@
}
/**
- * Remove from the list a callback that was previously added with
+ * Remove from the list an interface that was previously added with
* {@link #register}. This uses the
- * {@link IInterface#asBinder callback.asBinder()} object to correctly
+ * {@link IInterface#asBinder callbackInterface.asBinder()} object to correctly
* find the previous registration.
* Registrations are not counted; a single unregister call will remove
- * a callback after any number calls to {@link #register} for it.
+ * an interface after any number calls to {@link #register} for it.
*
- * @param callback The callback to be removed from the list. Passing
+ * @param callbackInterface The interface to be removed from the list. Passing
* null here will cause a NullPointerException, so you will generally want
* to check for null before calling.
*
- * @return Returns true if the callback was found and unregistered. Returns
- * false if the given callback was not found on the list.
+ * @return Returns true if the interface was found and unregistered. Returns
+ * false if the given interface was not found on the list.
*
* @see #register
*/
- public boolean unregister(E callback) {
- synchronized (mCallbacks) {
- Callback cb = mCallbacks.remove(callback.asBinder());
- if (cb != null) {
- cb.mCallback.asBinder().unlinkToDeath(cb, 0);
+ public boolean unregister(E callbackInterface) {
+ synchronized (mInterfaces) {
+ Interface i = mInterfaces.remove(callbackInterface.asBinder());
+ if (i != null) {
+ i.mInterface.asBinder().unlinkToDeath(i, 0);
+ i.maybeUnsubscribeFromFrozenCallback();
return true;
}
return false;
@@ -164,20 +453,21 @@
}
/**
- * Disable this callback list. All registered callbacks are unregistered,
+ * Disable this interface list. All registered interfaces are unregistered,
* and the list is disabled so that future calls to {@link #register} will
* fail. This should be used when a Service is stopping, to prevent clients
- * from registering callbacks after it is stopped.
+ * from registering interfaces after it is stopped.
*
* @see #register
*/
public void kill() {
- synchronized (mCallbacks) {
- for (int cbi=mCallbacks.size()-1; cbi>=0; cbi--) {
- Callback cb = mCallbacks.valueAt(cbi);
- cb.mCallback.asBinder().unlinkToDeath(cb, 0);
+ synchronized (mInterfaces) {
+ for (int cbi = mInterfaces.size() - 1; cbi >= 0; cbi--) {
+ Interface i = mInterfaces.valueAt(cbi);
+ i.mInterface.asBinder().unlinkToDeath(i, 0);
+ i.maybeUnsubscribeFromFrozenCallback();
}
- mCallbacks.clear();
+ mInterfaces.clear();
mKilled = true;
}
}
@@ -186,15 +476,15 @@
* Old version of {@link #onCallbackDied(E, Object)} that
* does not provide a cookie.
*/
- public void onCallbackDied(E callback) {
+ public void onCallbackDied(E callbackInterface) {
}
/**
- * Called when the process hosting a callback in the list has gone away.
+ * Called when the process hosting an interface in the list has gone away.
* The default implementation calls {@link #onCallbackDied(E)}
* for backwards compatibility.
*
- * @param callback The callback whose process has died. Note that, since
+ * @param callbackInterface The interface whose process has died. Note that, since
* its process has died, you can not make any calls on to this interface.
* You can, however, retrieve its IBinder and compare it with another
* IBinder to see if it is the same object.
@@ -203,13 +493,15 @@
*
* @see #register
*/
- public void onCallbackDied(E callback, Object cookie) {
- onCallbackDied(callback);
+ public void onCallbackDied(E callbackInterface, Object cookie) {
+ onCallbackDied(callbackInterface);
}
/**
- * Prepare to start making calls to the currently registered callbacks.
- * This creates a copy of the callback list, which you can retrieve items
+ * Use {@link #broadcast(Consumer)} instead to ensure proper handling of frozen processes.
+ *
+ * Prepare to start making calls to the currently registered interfaces.
+ * This creates a copy of the interface list, which you can retrieve items
* from using {@link #getBroadcastItem}. Note that only one broadcast can
* be active at a time, so you must be sure to always call this from the
* same thread (usually by scheduling with {@link Handler}) or
@@ -219,44 +511,56 @@
* <p>A typical loop delivering a broadcast looks like this:
*
* <pre>
- * int i = callbacks.beginBroadcast();
+ * int i = interfaces.beginBroadcast();
* while (i > 0) {
* i--;
* try {
- * callbacks.getBroadcastItem(i).somethingHappened();
+ * interfaces.getBroadcastItem(i).somethingHappened();
* } catch (RemoteException e) {
* // The RemoteCallbackList will take care of removing
* // the dead object for us.
* }
* }
- * callbacks.finishBroadcast();</pre>
+ * interfaces.finishBroadcast();</pre>
*
- * @return Returns the number of callbacks in the broadcast, to be used
+ * Note that this method is only supported for {@link #FROZEN_CALLEE_POLICY_UNSET}. For other
+ * policies use {@link #broadcast(Consumer)} instead.
+ *
+ * @return Returns the number of interfaces in the broadcast, to be used
* with {@link #getBroadcastItem} to determine the range of indices you
* can supply.
*
+ * @throws UnsupportedOperationException if an frozen callee policy is set.
+ *
* @see #getBroadcastItem
* @see #finishBroadcast
*/
public int beginBroadcast() {
- synchronized (mCallbacks) {
+ if (mFrozenCalleePolicy != FROZEN_CALLEE_POLICY_UNSET) {
+ throw new UnsupportedOperationException();
+ }
+ return beginBroadcastInternal();
+ }
+
+ private int beginBroadcastInternal() {
+ synchronized (mInterfaces) {
if (mBroadcastCount > 0) {
throw new IllegalStateException(
"beginBroadcast() called while already in a broadcast");
}
- final int N = mBroadcastCount = mCallbacks.size();
- if (N <= 0) {
+ final int n = mBroadcastCount = mInterfaces.size();
+ if (n <= 0) {
return 0;
}
Object[] active = mActiveBroadcast;
- if (active == null || active.length < N) {
- mActiveBroadcast = active = new Object[N];
+ if (active == null || active.length < n) {
+ mActiveBroadcast = active = new Object[n];
}
- for (int i=0; i<N; i++) {
- active[i] = mCallbacks.valueAt(i);
+ for (int i = 0; i < n; i++) {
+ active[i] = mInterfaces.valueAt(i);
}
- return N;
+ return n;
}
}
@@ -267,24 +571,23 @@
* calling {@link #finishBroadcast}.
*
* <p>Note that it is possible for the process of one of the returned
- * callbacks to go away before you call it, so you will need to catch
+ * interfaces to go away before you call it, so you will need to catch
* {@link RemoteException} when calling on to the returned object.
- * The callback list itself, however, will take care of unregistering
+ * The interface list itself, however, will take care of unregistering
* these objects once it detects that it is no longer valid, so you can
* handle such an exception by simply ignoring it.
*
- * @param index Which of the registered callbacks you would like to
+ * @param index Which of the registered interfaces you would like to
* retrieve. Ranges from 0 to {@link #beginBroadcast}-1, inclusive.
*
- * @return Returns the callback interface that you can call. This will
- * always be non-null.
+ * @return Returns the interface that you can call. This will always be non-null.
*
* @see #beginBroadcast
*/
public E getBroadcastItem(int index) {
- return ((Callback)mActiveBroadcast[index]).mCallback;
+ return ((Interface) mActiveBroadcast[index]).mInterface;
}
-
+
/**
* Retrieve the cookie associated with the item
* returned by {@link #getBroadcastItem(int)}.
@@ -292,7 +595,7 @@
* @see #getBroadcastItem
*/
public Object getBroadcastCookie(int index) {
- return ((Callback)mActiveBroadcast[index]).mCookie;
+ return ((Interface) mActiveBroadcast[index]).mCookie;
}
/**
@@ -303,7 +606,7 @@
* @see #beginBroadcast
*/
public void finishBroadcast() {
- synchronized (mCallbacks) {
+ synchronized (mInterfaces) {
if (mBroadcastCount < 0) {
throw new IllegalStateException(
"finishBroadcast() called outside of a broadcast");
@@ -322,16 +625,18 @@
}
/**
- * Performs {@code action} on each callback, calling
- * {@link #beginBroadcast()}/{@link #finishBroadcast()} before/after looping
+ * Performs {@code callback} on each registered interface.
*
- * @hide
+ * This is equivalent to #beginBroadcast, followed by iterating over the items using
+ * #getBroadcastItem and then @finishBroadcast, except that this method supports
+ * frozen callee policies.
*/
- public void broadcast(Consumer<E> action) {
- int itemCount = beginBroadcast();
+ @FlaggedApi(Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK)
+ public void broadcast(@NonNull Consumer<E> callback) {
+ int itemCount = beginBroadcastInternal();
try {
for (int i = 0; i < itemCount; i++) {
- action.accept(getBroadcastItem(i));
+ ((Interface) mActiveBroadcast[i]).addCallback(callback);
}
} finally {
finishBroadcast();
@@ -339,16 +644,16 @@
}
/**
- * Performs {@code action} for each cookie associated with a callback, calling
+ * Performs {@code callback} for each cookie associated with an interface, calling
* {@link #beginBroadcast()}/{@link #finishBroadcast()} before/after looping
*
* @hide
*/
- public <C> void broadcastForEachCookie(Consumer<C> action) {
+ public <C> void broadcastForEachCookie(Consumer<C> callback) {
int itemCount = beginBroadcast();
try {
for (int i = 0; i < itemCount; i++) {
- action.accept((C) getBroadcastCookie(i));
+ callback.accept((C) getBroadcastCookie(i));
}
} finally {
finishBroadcast();
@@ -356,16 +661,16 @@
}
/**
- * Performs {@code action} on each callback and associated cookie, calling {@link
+ * Performs {@code callback} on each interface and associated cookie, calling {@link
* #beginBroadcast()}/{@link #finishBroadcast()} before/after looping.
*
* @hide
*/
- public <C> void broadcast(BiConsumer<E, C> action) {
+ public <C> void broadcast(BiConsumer<E, C> callback) {
int itemCount = beginBroadcast();
try {
for (int i = 0; i < itemCount; i++) {
- action.accept(getBroadcastItem(i), (C) getBroadcastCookie(i));
+ callback.accept(getBroadcastItem(i), (C) getBroadcastCookie(i));
}
} finally {
finishBroadcast();
@@ -373,10 +678,10 @@
}
/**
- * Returns the number of registered callbacks. Note that the number of registered
- * callbacks may differ from the value returned by {@link #beginBroadcast()} since
- * the former returns the number of callbacks registered at the time of the call
- * and the second the number of callback to which the broadcast will be delivered.
+ * Returns the number of registered interfaces. Note that the number of registered
+ * interfaces may differ from the value returned by {@link #beginBroadcast()} since
+ * the former returns the number of interfaces registered at the time of the call
+ * and the second the number of interfaces to which the broadcast will be delivered.
* <p>
* This function is useful to decide whether to schedule a broadcast if this
* requires doing some work which otherwise would not be performed.
@@ -385,39 +690,39 @@
* @return The size.
*/
public int getRegisteredCallbackCount() {
- synchronized (mCallbacks) {
+ synchronized (mInterfaces) {
if (mKilled) {
return 0;
}
- return mCallbacks.size();
+ return mInterfaces.size();
}
}
/**
- * Return a currently registered callback. Note that this is
+ * Return a currently registered interface. Note that this is
* <em>not</em> the same as {@link #getBroadcastItem} and should not be used
- * interchangeably with it. This method returns the registered callback at the given
+ * interchangeably with it. This method returns the registered interface at the given
* index, not the current broadcast state. This means that it is not itself thread-safe:
* any call to {@link #register} or {@link #unregister} will change these indices, so you
* must do your own thread safety between these to protect from such changes.
*
- * @param index Index of which callback registration to return, from 0 to
+ * @param index Index of which interface registration to return, from 0 to
* {@link #getRegisteredCallbackCount()} - 1.
*
- * @return Returns whatever callback is associated with this index, or null if
+ * @return Returns whatever interface is associated with this index, or null if
* {@link #kill()} has been called.
*/
public E getRegisteredCallbackItem(int index) {
- synchronized (mCallbacks) {
+ synchronized (mInterfaces) {
if (mKilled) {
return null;
}
- return mCallbacks.valueAt(index).mCallback;
+ return mInterfaces.valueAt(index).mInterface;
}
}
/**
- * Return any cookie associated with a currently registered callback. Note that this is
+ * Return any cookie associated with a currently registered interface. Note that this is
* <em>not</em> the same as {@link #getBroadcastCookie} and should not be used
* interchangeably with it. This method returns the current cookie registered at the given
* index, not the current broadcast state. This means that it is not itself thread-safe:
@@ -431,25 +736,25 @@
* {@link #kill()} has been called.
*/
public Object getRegisteredCallbackCookie(int index) {
- synchronized (mCallbacks) {
+ synchronized (mInterfaces) {
if (mKilled) {
return null;
}
- return mCallbacks.valueAt(index).mCookie;
+ return mInterfaces.valueAt(index).mCookie;
}
}
/** @hide */
public void dump(PrintWriter pw, String prefix) {
- synchronized (mCallbacks) {
- pw.print(prefix); pw.print("callbacks: "); pw.println(mCallbacks.size());
+ synchronized (mInterfaces) {
+ pw.print(prefix); pw.print("callbacks: "); pw.println(mInterfaces.size());
pw.print(prefix); pw.print("killed: "); pw.println(mKilled);
pw.print(prefix); pw.print("broadcasts count: "); pw.println(mBroadcastCount);
}
}
- private void logExcessiveCallbacks() {
- final long size = mCallbacks.size();
+ private void logExcessiveInterfaces() {
+ final long size = mInterfaces.size();
final long TOO_MANY = 3000;
final long MAX_CHARS = 1000;
if (size >= TOO_MANY) {
diff --git a/core/java/android/security/flags.aconfig b/core/java/android/security/flags.aconfig
index 3f4f790..5d1d758 100644
--- a/core/java/android/security/flags.aconfig
+++ b/core/java/android/security/flags.aconfig
@@ -115,6 +115,14 @@
}
flag {
+ name: "protect_device_config_flags"
+ namespace: "psap_ai"
+ description: "Feature flag to limit adb shell to allowlisted flags"
+ bug: "364083026"
+ is_fixed_read_only: true
+}
+
+flag {
name: "keystore_grant_api"
namespace: "hardware_backed_security"
description: "Feature flag for exposing KeyStore grant APIs"
diff --git a/core/java/android/telephony/SubscriptionPlan.java b/core/java/android/telephony/SubscriptionPlan.java
index 7b48a16..4c59a85 100644
--- a/core/java/android/telephony/SubscriptionPlan.java
+++ b/core/java/android/telephony/SubscriptionPlan.java
@@ -18,6 +18,7 @@
import android.annotation.BytesLong;
import android.annotation.CurrentTimeMillisLong;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -28,6 +29,7 @@
import android.util.Range;
import android.util.RecurrenceRule;
+import com.android.internal.telephony.flags.Flags;
import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
@@ -83,6 +85,33 @@
/** Value indicating a timestamp is unknown. */
public static final long TIME_UNKNOWN = -1;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "SUBSCRIPTION_STATUS_" }, value = {
+ SUBSCRIPTION_STATUS_UNKNOWN,
+ SUBSCRIPTION_STATUS_ACTIVE,
+ SUBSCRIPTION_STATUS_INACTIVE,
+ SUBSCRIPTION_STATUS_TRIAL,
+ SUBSCRIPTION_STATUS_SUSPENDED
+ })
+ public @interface SubscriptionStatus {}
+
+ /** Subscription status is unknown. */
+ @FlaggedApi(Flags.FLAG_SUBSCRIPTION_PLAN_ALLOW_STATUS_AND_END_DATE)
+ public static final int SUBSCRIPTION_STATUS_UNKNOWN = 0;
+ /** Subscription is active. */
+ @FlaggedApi(Flags.FLAG_SUBSCRIPTION_PLAN_ALLOW_STATUS_AND_END_DATE)
+ public static final int SUBSCRIPTION_STATUS_ACTIVE = 1;
+ /** Subscription is inactive. */
+ @FlaggedApi(Flags.FLAG_SUBSCRIPTION_PLAN_ALLOW_STATUS_AND_END_DATE)
+ public static final int SUBSCRIPTION_STATUS_INACTIVE = 2;
+ /** Subscription is in a trial period. */
+ @FlaggedApi(Flags.FLAG_SUBSCRIPTION_PLAN_ALLOW_STATUS_AND_END_DATE)
+ public static final int SUBSCRIPTION_STATUS_TRIAL = 3;
+ /** Subscription is suspended. */
+ @FlaggedApi(Flags.FLAG_SUBSCRIPTION_PLAN_ALLOW_STATUS_AND_END_DATE)
+ public static final int SUBSCRIPTION_STATUS_SUSPENDED = 4;
+
private final RecurrenceRule cycleRule;
private CharSequence title;
private CharSequence summary;
@@ -91,6 +120,7 @@
private long dataUsageBytes = BYTES_UNKNOWN;
private long dataUsageTime = TIME_UNKNOWN;
private @NetworkType int[] networkTypes;
+ private int mSubscriptionStatus = SUBSCRIPTION_STATUS_UNKNOWN;
private SubscriptionPlan(RecurrenceRule cycleRule) {
this.cycleRule = Preconditions.checkNotNull(cycleRule);
@@ -107,6 +137,7 @@
dataUsageBytes = source.readLong();
dataUsageTime = source.readLong();
networkTypes = source.createIntArray();
+ mSubscriptionStatus = source.readInt();
}
@Override
@@ -124,6 +155,7 @@
dest.writeLong(dataUsageBytes);
dest.writeLong(dataUsageTime);
dest.writeIntArray(networkTypes);
+ dest.writeInt(mSubscriptionStatus);
}
@Override
@@ -137,13 +169,14 @@
.append(" dataUsageBytes=").append(dataUsageBytes)
.append(" dataUsageTime=").append(dataUsageTime)
.append(" networkTypes=").append(Arrays.toString(networkTypes))
+ .append(" subscriptionStatus=").append(mSubscriptionStatus)
.append("}").toString();
}
@Override
public int hashCode() {
return Objects.hash(cycleRule, title, summary, dataLimitBytes, dataLimitBehavior,
- dataUsageBytes, dataUsageTime, Arrays.hashCode(networkTypes));
+ dataUsageBytes, dataUsageTime, Arrays.hashCode(networkTypes), mSubscriptionStatus);
}
@Override
@@ -157,7 +190,8 @@
&& dataLimitBehavior == other.dataLimitBehavior
&& dataUsageBytes == other.dataUsageBytes
&& dataUsageTime == other.dataUsageTime
- && Arrays.equals(networkTypes, other.networkTypes);
+ && Arrays.equals(networkTypes, other.networkTypes)
+ && mSubscriptionStatus == other.mSubscriptionStatus;
}
return false;
}
@@ -179,6 +213,13 @@
return cycleRule;
}
+ /** Return the end date of this plan, or null if no end date exists. */
+ @FlaggedApi(Flags.FLAG_SUBSCRIPTION_PLAN_ALLOW_STATUS_AND_END_DATE)
+ public @Nullable ZonedDateTime getPlanEndDate() {
+ // ZonedDateTime is immutable, so no need to create a defensive copy.
+ return cycleRule.end;
+ }
+
/** Return the short title of this plan. */
public @Nullable CharSequence getTitle() {
return title;
@@ -238,6 +279,16 @@
}
/**
+ * Returns the status of the subscription plan.
+ *
+ * @return The subscription status, or {@link #SUBSCRIPTION_STATUS_UNKNOWN} if not available.
+ */
+ @FlaggedApi(Flags.FLAG_SUBSCRIPTION_PLAN_ALLOW_STATUS_AND_END_DATE)
+ public @SubscriptionStatus int getSubscriptionStatus() {
+ return mSubscriptionStatus;
+ }
+
+ /**
* Builder for a {@link SubscriptionPlan}.
*/
public static class Builder {
@@ -382,5 +433,21 @@
TelephonyManager.getAllNetworkTypes().length);
return this;
}
+
+ /**
+ * Set the subscription status.
+ *
+ * @param subscriptionStatus the current subscription status
+ */
+ @FlaggedApi(Flags.FLAG_SUBSCRIPTION_PLAN_ALLOW_STATUS_AND_END_DATE)
+ public @NonNull Builder setSubscriptionStatus(@SubscriptionStatus int subscriptionStatus) {
+ if (subscriptionStatus < SUBSCRIPTION_STATUS_UNKNOWN
+ || subscriptionStatus > SUBSCRIPTION_STATUS_SUSPENDED) {
+ throw new IllegalArgumentException(
+ "Subscription status must be defined with a valid value");
+ }
+ plan.mSubscriptionStatus = subscriptionStatus;
+ return this;
+ }
}
}
diff --git a/core/java/com/android/internal/compat/AndroidBuildClassifier.java b/core/java/com/android/internal/compat/AndroidBuildClassifier.java
index 364db06..19f8889 100644
--- a/core/java/com/android/internal/compat/AndroidBuildClassifier.java
+++ b/core/java/com/android/internal/compat/AndroidBuildClassifier.java
@@ -22,6 +22,7 @@
* Platform private class for determining the type of Android build installed.
*
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class AndroidBuildClassifier {
public boolean isDebuggableBuild() {
diff --git a/core/java/com/android/internal/compat/ChangeReporter.java b/core/java/com/android/internal/compat/ChangeReporter.java
index ded142c..bfa122b 100644
--- a/core/java/com/android/internal/compat/ChangeReporter.java
+++ b/core/java/com/android/internal/compat/ChangeReporter.java
@@ -42,6 +42,7 @@
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class ChangeReporter {
private static final String TAG = "CompatChangeReporter";
private static final Function<Integer, Set<ChangeReport>> NEW_CHANGE_REPORT_SET =
diff --git a/core/java/com/android/internal/compat/CompatibilityChangeConfig.java b/core/java/com/android/internal/compat/CompatibilityChangeConfig.java
index 182dba7..8fd914ae 100644
--- a/core/java/com/android/internal/compat/CompatibilityChangeConfig.java
+++ b/core/java/com/android/internal/compat/CompatibilityChangeConfig.java
@@ -28,6 +28,7 @@
* Parcelable containing compat config overrides for a given application.
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class CompatibilityChangeConfig implements Parcelable {
private final ChangeConfig mChangeConfig;
diff --git a/core/java/com/android/internal/compat/CompatibilityChangeInfo.java b/core/java/com/android/internal/compat/CompatibilityChangeInfo.java
index 03fe455..505fd23 100644
--- a/core/java/com/android/internal/compat/CompatibilityChangeInfo.java
+++ b/core/java/com/android/internal/compat/CompatibilityChangeInfo.java
@@ -25,6 +25,7 @@
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class CompatibilityChangeInfo implements Parcelable {
private final long mChangeId;
private final @Nullable String mName;
diff --git a/core/java/com/android/internal/compat/CompatibilityOverrideConfig.java b/core/java/com/android/internal/compat/CompatibilityOverrideConfig.java
index 9a02b7b..32206c9 100644
--- a/core/java/com/android/internal/compat/CompatibilityOverrideConfig.java
+++ b/core/java/com/android/internal/compat/CompatibilityOverrideConfig.java
@@ -28,6 +28,7 @@
* Parcelable containing compat config overrides for a given application.
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class CompatibilityOverrideConfig implements Parcelable {
public final Map<Long, PackageOverride> overrides;
diff --git a/core/java/com/android/internal/compat/CompatibilityOverridesByPackageConfig.java b/core/java/com/android/internal/compat/CompatibilityOverridesByPackageConfig.java
index 8652bb6..998b48a 100644
--- a/core/java/com/android/internal/compat/CompatibilityOverridesByPackageConfig.java
+++ b/core/java/com/android/internal/compat/CompatibilityOverridesByPackageConfig.java
@@ -26,6 +26,7 @@
* Parcelable containing compat config overrides by application.
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class CompatibilityOverridesByPackageConfig implements Parcelable {
public final Map<String, CompatibilityOverrideConfig> packageNameToOverrides;
diff --git a/core/java/com/android/internal/compat/CompatibilityOverridesToRemoveByPackageConfig.java b/core/java/com/android/internal/compat/CompatibilityOverridesToRemoveByPackageConfig.java
index b408d64..c0e2217 100644
--- a/core/java/com/android/internal/compat/CompatibilityOverridesToRemoveByPackageConfig.java
+++ b/core/java/com/android/internal/compat/CompatibilityOverridesToRemoveByPackageConfig.java
@@ -29,6 +29,7 @@
* IDs.
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class CompatibilityOverridesToRemoveByPackageConfig implements Parcelable {
public final Map<String, CompatibilityOverridesToRemoveConfig> packageNameToOverridesToRemove;
diff --git a/core/java/com/android/internal/compat/CompatibilityOverridesToRemoveConfig.java b/core/java/com/android/internal/compat/CompatibilityOverridesToRemoveConfig.java
index e85afef..10461ec 100644
--- a/core/java/com/android/internal/compat/CompatibilityOverridesToRemoveConfig.java
+++ b/core/java/com/android/internal/compat/CompatibilityOverridesToRemoveConfig.java
@@ -30,6 +30,7 @@
* <p>This class is separate from CompatibilityOverrideConfig since we only need change IDs.
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class CompatibilityOverridesToRemoveConfig implements Parcelable {
public final Set<Long> changeIds;
diff --git a/core/java/com/android/internal/compat/OverrideAllowedState.java b/core/java/com/android/internal/compat/OverrideAllowedState.java
index e408be2..f018c3a 100644
--- a/core/java/com/android/internal/compat/OverrideAllowedState.java
+++ b/core/java/com/android/internal/compat/OverrideAllowedState.java
@@ -27,6 +27,7 @@
/**
* This class contains all the possible override allowed states.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class OverrideAllowedState implements Parcelable {
@IntDef({
ALLOWED,
diff --git a/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java b/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java
index a69d2e4..8df3f2a 100644
--- a/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java
+++ b/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java
@@ -15,6 +15,12 @@
*/
package com.android.internal.ravenwood;
+import static android.os.Build.VERSION_CODES.S;
+import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
+
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.Disabled;
+import android.compat.annotation.EnabledAfter;
import android.ravenwood.annotation.RavenwoodKeepWholeClass;
import android.ravenwood.annotation.RavenwoodRedirect;
import android.ravenwood.annotation.RavenwoodRedirectionClass;
@@ -78,4 +84,24 @@
public String getRavenwoodRuntimePath() {
throw notSupportedOnDevice();
}
+
+ /** @hide */
+ public static class CompatIdsForTest {
+ // Enabled by default
+ /** Used for testing */
+ @ChangeId
+ public static final long TEST_COMPAT_ID_1 = 368131859L;
+
+ /** Used for testing */
+ @Disabled
+ @ChangeId public static final long TEST_COMPAT_ID_2 = 368131701L;
+
+ /** Used for testing */
+ @EnabledAfter(targetSdkVersion = S)
+ @ChangeId public static final long TEST_COMPAT_ID_3 = 368131659L;
+
+ /** Used for testing */
+ @EnabledAfter(targetSdkVersion = UPSIDE_DOWN_CAKE)
+ @ChangeId public static final long TEST_COMPAT_ID_4 = 368132057L;
+ }
}
diff --git a/keystore/java/Android.bp b/keystore/java/Android.bp
index 21edff1..264ac5ff 100644
--- a/keystore/java/Android.bp
+++ b/keystore/java/Android.bp
@@ -13,5 +13,13 @@
"**/*.java",
"**/*.aidl",
],
+ exclude_srcs: select(release_flag("RELEASE_ATTEST_MODULES"), {
+ true: [
+ "android/security/KeyStore2HalCurrent.java",
+ ],
+ default: [
+ "android/security/KeyStore2HalLatest.java",
+ ],
+ }),
visibility: ["//frameworks/base"],
}
diff --git a/keystore/java/android/security/KeyStore2.java b/keystore/java/android/security/KeyStore2.java
index dd703f5..f5cf571 100644
--- a/keystore/java/android/security/KeyStore2.java
+++ b/keystore/java/android/security/KeyStore2.java
@@ -101,7 +101,7 @@
R execute(IKeystoreService service) throws RemoteException;
}
- private <R> R handleRemoteExceptionWithRetry(@NonNull CheckedRemoteRequest<R> request)
+ <R> R handleRemoteExceptionWithRetry(@NonNull CheckedRemoteRequest<R> request)
throws KeyStoreException {
IKeystoreService service = getService(false /* retryLookup */);
boolean firstTry = true;
@@ -369,6 +369,18 @@
}
}
+ /**
+ * Returns tag-specific info required to interpret a tag's attested value.
+ * @see IKeystoreService#getSupplementaryAttestationInfo(Tag) for more details.
+ * @param tag
+ * @return
+ * @throws KeyStoreException
+ * @hide
+ */
+ public byte[] getSupplementaryAttestationInfo(int tag) throws KeyStoreException {
+ return KeyStore2HalVersion.getSupplementaryAttestationInfoHelper(tag, this);
+ }
+
static KeyStoreException getKeyStoreException(int errorCode, String serviceErrorMessage) {
if (errorCode > 0) {
// KeyStore layer error
diff --git a/keystore/java/android/security/KeyStore2HalCurrent.java b/keystore/java/android/security/KeyStore2HalCurrent.java
new file mode 100644
index 0000000..f4d8fe6
--- /dev/null
+++ b/keystore/java/android/security/KeyStore2HalCurrent.java
@@ -0,0 +1,30 @@
+/*
+ * 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.security;
+
+/**
+ * @hide This class is necessary to allow the version of the AIDL interface for Keystore and
+* KeyMint used in KeyStore2.java to differ by BUILD flag `RELEASE_ATTEST_MODULES`. When
+* `RELEASE_ATTEST_MODULES` is not set, this file is included, and the current HALs for Keystore
+* (V4) and KeyMint (V3) are used.
+*/
+class KeyStore2HalVersion {
+ public static byte[] getSupplementaryAttestationInfoHelper(int tag, KeyStore2 ks)
+ throws KeyStoreException {
+ return new byte[0];
+ }
+}
diff --git a/keystore/java/android/security/KeyStore2HalLatest.java b/keystore/java/android/security/KeyStore2HalLatest.java
new file mode 100644
index 0000000..b6e1cbb
--- /dev/null
+++ b/keystore/java/android/security/KeyStore2HalLatest.java
@@ -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 android.security;
+
+/**
+ * @hide This class is necessary to allow the version of the AIDL interface for Keystore and
+* KeyMint used in KeyStore2.java to differ by BUILD flag `RELEASE_ATTEST_MODULES`. When
+* `RELEASE_ATTEST_MODULES` is set, this file is included, and the latest HALs for Keystore (V5)
+* and KeyMint (V4) are used.
+*/
+class KeyStore2HalVersion {
+ public byte[] getSupplementaryAttestationInfoHelper(int tag, KeyStore2 ks)
+ throws KeyStoreException {
+ return ks.handleRemoteExceptionWithRetry(
+ (service) -> service.getSupplementaryAttestationInfo(tag));
+ }
+}
diff --git a/keystore/java/android/security/keystore/KeyStoreManager.java b/keystore/java/android/security/keystore/KeyStoreManager.java
index e6091c1..740ccb5 100644
--- a/keystore/java/android/security/keystore/KeyStoreManager.java
+++ b/keystore/java/android/security/keystore/KeyStoreManager.java
@@ -17,9 +17,11 @@
package android.security.keystore;
import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemService;
import android.content.Context;
+import android.hardware.security.keymint.TagType;
import android.security.KeyStore2;
import android.security.KeyStoreException;
import android.security.keystore2.AndroidKeyStoreProvider;
@@ -32,6 +34,8 @@
import com.android.internal.annotations.GuardedBy;
import java.io.ByteArrayInputStream;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.security.Key;
import java.security.KeyPair;
import java.security.PublicKey;
@@ -299,6 +303,37 @@
return Collections.emptyList();
}
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value = {MODULE_HASH})
+ public @interface SupplementaryAttestationInfoTagEnum {}
+
+ /**
+ * When passed into getSupplementaryAttestationInfo, getSupplementaryAttestationInfo returns the
+ * DER-encoded structure corresponding to the `Modules` schema described in the KeyMint HAL's
+ * KeyCreationResult.aidl. The SHA-256 hash of this encoded structure is what's included with
+ * the tag in attestations.
+ */
+ // TODO(b/369375199): Replace with Tag.MODULE_HASH when flagging is removed.
+ public static final int MODULE_HASH = TagType.BYTES | 724;
+
+ /**
+ * Returns tag-specific data required to interpret a tag's attested value.
+ *
+ * When performing key attestation, the obtained attestation certificate contains a list of tags
+ * and their corresponding attested values. For some tags, additional information about the
+ * attested value can be queried via this API. See individual tags for specifics.
+ *
+ * @param tag tag for which info is being requested
+ * @return tag-specific info
+ * @throws KeyStoreException if the requested info is not available
+ */
+ @FlaggedApi(android.security.keystore2.Flags.FLAG_ATTEST_MODULES)
+ public @NonNull byte[] getSupplementaryAttestationInfo(
+ @SupplementaryAttestationInfoTagEnum int tag) throws KeyStoreException {
+ return mKeyStore2.getSupplementaryAttestationInfo(tag);
+ }
+
/**
* Returns a new {@link KeyDescriptor} instance in the app domain / namespace with the {@code
* alias} set to the provided value.
diff --git a/native/graphics/jni/Android.bp b/native/graphics/jni/Android.bp
index 0fb3049..962f54d 100644
--- a/native/graphics/jni/Android.bp
+++ b/native/graphics/jni/Android.bp
@@ -45,8 +45,10 @@
header_libs: [
"jni_headers",
+ "native_headers",
"libhwui_internal_headers",
],
+ export_header_lib_headers: ["native_headers"],
static_libs: ["libarect"],
diff --git a/nfc/api/system-current.txt b/nfc/api/system-current.txt
index c25f77b..f587660 100644
--- a/nfc/api/system-current.txt
+++ b/nfc/api/system-current.txt
@@ -57,6 +57,7 @@
@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.List<java.lang.String> getActiveNfceeList();
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();
@@ -73,6 +74,9 @@
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
@@ -89,13 +93,14 @@
method public void onBootFinished(int);
method public void onBootStarted();
method public void onCardEmulationActivated(boolean);
- method public void onDisable(@NonNull java.util.function.Consumer<java.lang.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 onEnable(@NonNull java.util.function.Consumer<java.lang.Boolean>);
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);
@@ -106,7 +111,7 @@
method public void onReaderOptionChanged(boolean);
method public void onRfDiscoveryStarted(boolean);
method public void onRfFieldActivated(boolean);
- method public void onRoutingChanged();
+ 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);
@@ -183,6 +188,12 @@
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") public void overrideRoutingTable(@NonNull android.app.Activity, int, int);
method @FlaggedApi("android.nfc.nfc_override_recover_routing_table") public void recoverRoutingTable(@NonNull android.app.Activity);
+ 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
}
}
diff --git a/nfc/java/android/nfc/INfcAdapter.aidl b/nfc/java/android/nfc/INfcAdapter.aidl
index 40fd068..31514a0 100644
--- a/nfc/java/android/nfc/INfcAdapter.aidl
+++ b/nfc/java/android/nfc/INfcAdapter.aidl
@@ -120,4 +120,5 @@
boolean isTagPresent();
List<Entry> getRoutingTableEntryList();
void indicateDataMigration(boolean inProgress, String pkg);
+ int commitRouting();
}
diff --git a/nfc/java/android/nfc/INfcCardEmulation.aidl b/nfc/java/android/nfc/INfcCardEmulation.aidl
index 5e2e92d..633d8bf 100644
--- a/nfc/java/android/nfc/INfcCardEmulation.aidl
+++ b/nfc/java/android/nfc/INfcCardEmulation.aidl
@@ -47,7 +47,7 @@
boolean unsetPreferredService();
boolean supportsAidPrefixRegistration();
ApduServiceInfo getPreferredPaymentService(int userHandle);
- boolean setServiceEnabledForCategoryOther(int userHandle, in ComponentName app, boolean status);
+ int setServiceEnabledForCategoryOther(int userHandle, in ComponentName app, boolean status);
boolean isDefaultPaymentRegistered();
void overrideRoutingTable(int userHandle, String protocol, String technology, in String pkg);
diff --git a/nfc/java/android/nfc/INfcOemExtensionCallback.aidl b/nfc/java/android/nfc/INfcOemExtensionCallback.aidl
index fb793b0..1a21c0b 100644
--- a/nfc/java/android/nfc/INfcOemExtensionCallback.aidl
+++ b/nfc/java/android/nfc/INfcOemExtensionCallback.aidl
@@ -41,7 +41,7 @@
void onEnableFinished(int status);
void onDisableFinished(int status);
void onTagDispatch(in ResultReceiver isSkipped);
- void onRoutingChanged();
+ void onRoutingChanged(in ResultReceiver isSkipped);
void onHceEventReceived(int action);
void onReaderOptionChanged(boolean enabled);
void onCardEmulationActivated(boolean isActivated);
@@ -54,4 +54,5 @@
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/NfcOemExtension.java b/nfc/java/android/nfc/NfcOemExtension.java
index 57ee981..326ca64 100644
--- a/nfc/java/android/nfc/NfcOemExtension.java
+++ b/nfc/java/android/nfc/NfcOemExtension.java
@@ -23,11 +23,11 @@
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.SuppressLint;
import android.annotation.SystemApi;
import android.content.ComponentName;
import android.content.Context;
@@ -194,6 +194,30 @@
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 {
@@ -233,8 +257,7 @@
* {@link Boolean#TRUE}, otherwise call with {@link Boolean#FALSE}.
* false if NFC cannot be enabled at this time.
*/
- @SuppressLint("MethodNameTense")
- void onEnable(@NonNull Consumer<Boolean> isAllowed);
+ 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,
@@ -242,7 +265,7 @@
* {@link Boolean#TRUE}, otherwise call with {@link Boolean#FALSE}.
* false if NFC cannot be disabled at this time.
*/
- void onDisable(@NonNull Consumer<Boolean> isAllowed);
+ void onDisableRequested(@NonNull Consumer<Boolean> isAllowed);
/**
* Callback to indicate that Nfc starts to boot.
@@ -255,7 +278,7 @@
void onEnableStarted();
/**
- * Callback to indicate that Nfc starts to enable.
+ * Callback to indicate that Nfc starts to disable.
*/
void onDisableStarted();
@@ -287,8 +310,12 @@
/**
* 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();
+ void onRoutingChanged(@NonNull Consumer<Boolean> isCommitRoutingSkipped);
/**
* API to activate start stop cpu boost on hce event.
@@ -405,6 +432,19 @@
* @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);
}
@@ -605,12 +645,12 @@
/**
* 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.
- * @param timeoutInMs the pause polling duration in millisecond
+ * use {@link #resumePolling()} to resume the polling.
+ * @param timeoutInMs the pause polling duration in millisecond, ranging from 0 to 40000.
*/
@FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
@RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
- public void pausePolling(int timeoutInMs) {
+ public void pausePolling(@DurationMillisLong int timeoutInMs) {
NfcAdapter.callService(() -> NfcAdapter.sService.pausePolling(timeoutInMs));
}
@@ -741,6 +781,18 @@
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
@@ -799,13 +851,13 @@
public void onEnable(ResultReceiver isAllowed) throws RemoteException {
mCallbackMap.forEach((cb, ex) ->
handleVoidCallback(
- new ReceiverWrapper<>(isAllowed), cb::onEnable, ex));
+ new ReceiverWrapper<>(isAllowed), cb::onEnableRequested, ex));
}
@Override
public void onDisable(ResultReceiver isAllowed) throws RemoteException {
mCallbackMap.forEach((cb, ex) ->
handleVoidCallback(
- new ReceiverWrapper<>(isAllowed), cb::onDisable, ex));
+ new ReceiverWrapper<>(isAllowed), cb::onDisableRequested, ex));
}
@Override
public void onBootStarted() throws RemoteException {
@@ -844,9 +896,10 @@
new ReceiverWrapper<>(isSkipped), cb::onTagDispatch, ex));
}
@Override
- public void onRoutingChanged() throws RemoteException {
+ public void onRoutingChanged(ResultReceiver isSkipped) throws RemoteException {
mCallbackMap.forEach((cb, ex) ->
- handleVoidCallback(null, (Object input) -> cb.onRoutingChanged(), ex));
+ handleVoidCallback(
+ new ReceiverWrapper<>(isSkipped), cb::onRoutingChanged, ex));
}
@Override
public void onHceEventReceived(int action) throws RemoteException {
@@ -925,6 +978,15 @@
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) {
@@ -1035,8 +1097,14 @@
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
diff --git a/nfc/java/android/nfc/cardemulation/CardEmulation.java b/nfc/java/android/nfc/cardemulation/CardEmulation.java
index eb28c3b..8917524 100644
--- a/nfc/java/android/nfc/cardemulation/CardEmulation.java
+++ b/nfc/java/android/nfc/cardemulation/CardEmulation.java
@@ -185,6 +185,65 @@
@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 {}
+
static boolean sIsInitialized = false;
static HashMap<Context, CardEmulation> sCardEmus = new HashMap<Context, CardEmulation>();
static INfcCardEmulation sService;
@@ -883,22 +942,24 @@
}
/**
- * Allows to set or unset preferred service (category other) to avoid AID Collision.
+ * 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
- * @param userId the user handle of the user whose information is being requested.
- * @return set service for the category and true if service is already set return false.
+ * @return true if preferred service is successfully set or unset, otherwise return false.
*
* @hide
*/
- public boolean setServiceEnabledForCategoryOther(ComponentName service, boolean status,
- int userId) {
- if (service == null) {
- throw new NullPointerException("activity or service or category is null");
- }
+ @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(userId, service, status), false);
+ sService.setServiceEnabledForCategoryOther(mContext.getUser().getIdentifier(),
+ service, status), SET_SERVICE_ENABLED_STATUS_FAILURE_UNKNOWN_ERROR);
}
/** @hide */
diff --git a/nfc/java/android/nfc/flags.aconfig b/nfc/java/android/nfc/flags.aconfig
index 34f0200..8a37aa2 100644
--- a/nfc/java/android/nfc/flags.aconfig
+++ b/nfc/java/android/nfc/flags.aconfig
@@ -173,3 +173,11 @@
description: "Share wallet role routing priority with associated services"
bug: "366243361"
}
+
+flag {
+ name: "nfc_set_service_enabled_for_category_other"
+ is_exported: true
+ namespace: "nfc"
+ description: "Enable set service enabled for category other"
+ bug: "338157113"
+}
diff --git a/ravenwood/Framework.bp b/ravenwood/Framework.bp
index d207738..99fc31b 100644
--- a/ravenwood/Framework.bp
+++ b/ravenwood/Framework.bp
@@ -214,7 +214,8 @@
java_genrule {
name: "services.core.ravenwood",
- defaults: ["ravenwood-internal-only-visibility-genrule"],
+ // This is used by unit tests too (so tests will be able to access HSG-processed implementation)
+ // so it's visible to all.
cmd: "cp $(in) $(out)",
srcs: [
":services.core.ravenwood-base{ravenwood.jar}",
diff --git a/ravenwood/TEST_MAPPING b/ravenwood/TEST_MAPPING
index a1243e3..607592b 100644
--- a/ravenwood/TEST_MAPPING
+++ b/ravenwood/TEST_MAPPING
@@ -117,7 +117,11 @@
"host": true
},
{
- "name": "FrameworksServicesTestsRavenwood",
+ "name": "FrameworksServicesTestsRavenwood_Compat",
+ "host": true
+ },
+ {
+ "name": "FrameworksServicesTestsRavenwood_Uri",
"host": true
},
{
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
index c5a9c7b..678a97b 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
@@ -30,9 +30,12 @@
import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.app.AppCompatCallbacks;
import android.app.Instrumentation;
import android.app.ResourcesManager;
import android.app.UiAutomation;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
import android.content.res.Resources;
import android.os.Binder;
import android.os.Build;
@@ -41,6 +44,7 @@
import android.os.Looper;
import android.os.Process_ravenwood;
import android.os.ServiceManager;
+import android.os.ServiceManager.ServiceNotFoundException;
import android.os.SystemProperties;
import android.provider.DeviceConfig_host;
import android.system.ErrnoException;
@@ -58,6 +62,7 @@
import com.android.ravenwood.common.RavenwoodRuntimeException;
import com.android.ravenwood.common.SneakyThrow;
import com.android.server.LocalServices;
+import com.android.server.compat.PlatformCompat;
import org.junit.runner.Description;
@@ -331,6 +336,8 @@
RavenwoodSystemServer.init(config);
+ initializeCompatIds(config);
+
if (ENABLE_TIMEOUT_STACKS) {
sPendingTimeout = sTimeoutExecutor.schedule(
RavenwoodRuntimeEnvironmentController::dumpStacks,
@@ -346,6 +353,31 @@
Binder.restoreCallingIdentity(packBinderIdentityToken(false, config.mUid, config.mPid));
}
+ private static void initializeCompatIds(RavenwoodConfig config) {
+ // Set up compat-IDs for the app side.
+ // TODO: Inside the system server, all the compat-IDs should be enabled,
+ // Due to the `AppCompatCallbacks.install(new long[0], new long[0])` call in
+ // SystemServer.
+
+ // Compat framework only uses the package name and the target SDK level.
+ ApplicationInfo appInfo = new ApplicationInfo();
+ appInfo.packageName = config.mTargetPackageName;
+ appInfo.targetSdkVersion = config.mTargetSdkLevel;
+
+ PlatformCompat platformCompat = null;
+ try {
+ platformCompat = (PlatformCompat) ServiceManager.getServiceOrThrow(
+ Context.PLATFORM_COMPAT_SERVICE);
+ } catch (ServiceNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+
+ var disabledChanges = platformCompat.getDisabledChanges(appInfo);
+ var loggableChanges = platformCompat.getLoggableChanges(appInfo);
+
+ AppCompatCallbacks.install(disabledChanges, loggableChanges);
+ }
+
/**
* De-initialize.
*/
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java
index f198a08..438a2bf 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java
@@ -17,7 +17,9 @@
package android.platform.test.ravenwood;
import android.content.ClipboardManager;
+import android.content.Context;
import android.hardware.SerialManager;
+import android.os.ServiceManager;
import android.os.SystemClock;
import android.ravenwood.example.BlueManager;
import android.ravenwood.example.RedManager;
@@ -27,6 +29,8 @@
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.SystemServiceManager;
+import com.android.server.compat.PlatformCompat;
+import com.android.server.compat.PlatformCompatNative;
import com.android.server.utils.TimingsTraceAndSlog;
import java.util.List;
@@ -65,6 +69,14 @@
private static SystemServiceManager sServiceManager;
public static void init(RavenwoodConfig config) {
+ // Always start PlatformCompat, regardless of the requested services.
+ // PlatformCompat is not really a SystemService, so it won't receive boot phases / etc.
+ // This initialization code is copied from SystemServer.java.
+ PlatformCompat platformCompat = new PlatformCompat(config.mState.mSystemServerContext);
+ ServiceManager.addService(Context.PLATFORM_COMPAT_SERVICE, platformCompat);
+ ServiceManager.addService(Context.PLATFORM_COMPAT_NATIVE_SERVICE,
+ new PlatformCompatNative(platformCompat));
+
// Avoid overhead if no services required
if (config.mServicesRequired.isEmpty()) return;
diff --git a/ravenwood/scripts/run-ravenwood-tests.sh b/ravenwood/scripts/run-ravenwood-tests.sh
index fe2269a..27c5ea1 100755
--- a/ravenwood/scripts/run-ravenwood-tests.sh
+++ b/ravenwood/scripts/run-ravenwood-tests.sh
@@ -33,7 +33,7 @@
exclude_re=""
smoke_exclude_re=""
dry_run=""
-while getopts "sx:f:dt" opt; do
+while getopts "sx:f:dtb" opt; do
case "$opt" in
s)
# Remove slow tests.
@@ -52,8 +52,13 @@
dry_run="echo"
;;
t)
+ # Redirect log to terminal
export RAVENWOOD_LOG_OUT=$(tty)
;;
+ b)
+ # Build only
+ ATEST=m
+ ;;
'?')
exit 1
;;
@@ -99,11 +104,16 @@
# Calculate the removed tests.
-diff="$(diff <(echo "${all_tests[@]}" | tr ' ' '\n') <(echo "${targets[@]}" | tr ' ' '\n') )"
+diff="$(diff <(echo "${all_tests[@]}" | tr ' ' '\n') <(echo "${targets[@]}" | tr ' ' '\n') | grep -v [0-9] )"
if [[ "$diff" != "" ]]; then
echo "Excluded tests:"
echo "$diff"
fi
-$dry_run ${ATEST:-atest} "${targets[@]}"
+run() {
+ echo "Running: ${@}"
+ "${@}"
+}
+
+run $dry_run ${ATEST:-atest} "${targets[@]}"
diff --git a/ravenwood/tests/bivalenttest/Android.bp b/ravenwood/tests/bivalenttest/Android.bp
index 4895a1a..40e6672 100644
--- a/ravenwood/tests/bivalenttest/Android.bp
+++ b/ravenwood/tests/bivalenttest/Android.bp
@@ -58,6 +58,9 @@
java_defaults {
name: "ravenwood-bivalent-device-defaults",
defaults: ["ravenwood-bivalent-defaults"],
+
+ target_sdk_version: "34", // For compat-framework tests
+
// TODO(b/371215487): migrate bivalenttest.ravenizer tests to another architecture
exclude_srcs: [
"test/**/ravenizer/*.java",
diff --git a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/compat/RavenwoodCompatFrameworkTest.kt b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/compat/RavenwoodCompatFrameworkTest.kt
new file mode 100644
index 0000000..a95760d
--- /dev/null
+++ b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/compat/RavenwoodCompatFrameworkTest.kt
@@ -0,0 +1,56 @@
+/*
+ * 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.ravenwoodtest.bivalenttest.compat
+
+import android.app.compat.CompatChanges
+import android.os.Build
+import android.platform.test.ravenwood.RavenwoodConfig
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.internal.ravenwood.RavenwoodEnvironment.CompatIdsForTest
+import org.junit.Assert
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class RavenwoodCompatFrameworkTest {
+ companion object {
+ @JvmField // Expose as a raw field, not as a property.
+ @RavenwoodConfig.Config
+ val config = RavenwoodConfig.Builder()
+ .setTargetSdkLevel(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ .build()
+ }
+
+ @Test
+ fun testEnabled() {
+ Assert.assertTrue(CompatChanges.isChangeEnabled(CompatIdsForTest.TEST_COMPAT_ID_1))
+ }
+
+ @Test
+ fun testDisabled() {
+ Assert.assertFalse(CompatChanges.isChangeEnabled(CompatIdsForTest.TEST_COMPAT_ID_2))
+ }
+
+ @Test
+ fun testEnabledAfterSForUApps() {
+ Assert.assertTrue(CompatChanges.isChangeEnabled(CompatIdsForTest.TEST_COMPAT_ID_3))
+ }
+
+ @Test
+ fun testEnabledAfterUForUApps() {
+ Assert.assertFalse(CompatChanges.isChangeEnabled(CompatIdsForTest.TEST_COMPAT_ID_4))
+ }
+}
\ No newline at end of file
diff --git a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
index 5d24c3a..39e0cf9 100644
--- a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
+++ b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
@@ -366,3 +366,9 @@
android.os.IpcDataCache
android.app.PropertyInvalidatedCache
+
+android.app.compat.*
+com.android.server.compat.*
+com.android.internal.compat.*
+android.app.AppCompatCallbacks
+
diff --git a/ravenwood/texts/ravenwood-services-policies.txt b/ravenwood/texts/ravenwood-services-policies.txt
index cc2fa60..530e5c8 100644
--- a/ravenwood/texts/ravenwood-services-policies.txt
+++ b/ravenwood/texts/ravenwood-services-policies.txt
@@ -1 +1,12 @@
# Ravenwood "policy" file for services.core.
+
+# Auto-generated from XSD
+class com.android.server.compat.config.Change keepclass
+class com.android.server.compat.config.Config keepclass
+class com.android.server.compat.config.XmlParser keepclass
+class com.android.server.compat.overrides.ChangeOverrides keepclass
+class com.android.server.compat.overrides.OverrideValue keepclass
+class com.android.server.compat.overrides.Overrides keepclass
+class com.android.server.compat.overrides.RawOverrideValue keepclass
+class com.android.server.compat.overrides.XmlParser keepclass
+class com.android.server.compat.overrides.XmlWriter keepclass
\ No newline at end of file
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 0e81eb9..c31a4fb 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -95,6 +95,7 @@
import java.util.Random;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Supplier;
/**
@@ -126,7 +127,7 @@
IGateKeeperService mGateKeeper;
// Get and cache the available biometric authenticators and their associated info.
- final ArrayList<BiometricSensor> mSensors = new ArrayList<>();
+ final CopyOnWriteArrayList<BiometricSensor> mSensors = new CopyOnWriteArrayList<>();
@VisibleForTesting
BiometricStrengthController mBiometricStrengthController;
@@ -150,13 +151,13 @@
@NonNull private final Set<Integer> mSensorsPendingInvalidation;
public static InvalidationTracker start(@NonNull Context context,
- @NonNull ArrayList<BiometricSensor> sensors,
- int userId, int fromSensorId, @NonNull IInvalidationCallback clientCallback) {
+ @NonNull List<BiometricSensor> sensors, int userId,
+ int fromSensorId, @NonNull IInvalidationCallback clientCallback) {
return new InvalidationTracker(context, sensors, userId, fromSensorId, clientCallback);
}
private InvalidationTracker(@NonNull Context context,
- @NonNull ArrayList<BiometricSensor> sensors, int userId,
+ @NonNull List<BiometricSensor> sensors, int userId,
int fromSensorId, @NonNull IInvalidationCallback clientCallback) {
mClientCallback = clientCallback;
mSensorsPendingInvalidation = new ArraySet<>();
@@ -692,7 +693,7 @@
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override
- public synchronized void registerAuthenticator(int id, int modality,
+ public void registerAuthenticator(int id, int modality,
@Authenticators.Types int strength,
@NonNull IBiometricAuthenticator authenticator) {
diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java
index a40dd79..c3d88e3 100644
--- a/services/core/java/com/android/server/compat/CompatChange.java
+++ b/services/core/java/com/android/server/compat/CompatChange.java
@@ -50,6 +50,7 @@
*
* <p>Note, this class is not thread safe so callers must ensure thread safety.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class CompatChange extends CompatibilityChangeInfo {
/**
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index 79025d0..e89f43b 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -42,6 +42,7 @@
import com.android.internal.compat.CompatibilityOverridesToRemoveConfig;
import com.android.internal.compat.IOverrideValidator;
import com.android.internal.compat.OverrideAllowedState;
+import com.android.internal.ravenwood.RavenwoodEnvironment;
import com.android.server.compat.config.Change;
import com.android.server.compat.config.Config;
import com.android.server.compat.overrides.ChangeOverrides;
@@ -63,6 +64,7 @@
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Predicate;
import javax.xml.datatype.DatatypeConfigurationException;
@@ -72,12 +74,16 @@
* <p>It stores the default configuration for each change, and any per-package overrides that have
* been configured.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
final class CompatConfig {
private static final String TAG = "CompatConfig";
private static final String APP_COMPAT_DATA_DIR = "/data/misc/appcompat";
private static final String STATIC_OVERRIDES_PRODUCT_DIR = "/product/etc/appcompat";
private static final String OVERRIDES_FILE = "compat_framework_overrides.xml";
+ private static final String APP_COMPAT_DATA_DIR_RAVENWOOD = "/ravenwood-data/";
+ private static final String OVERRIDES_FILE_RAVENWOOD = "compat-config.xml";
+
private final ConcurrentHashMap<Long, CompatChange> mChanges = new ConcurrentHashMap<>();
private final OverrideValidatorImpl mOverrideValidator;
@@ -98,19 +104,32 @@
static CompatConfig create(AndroidBuildClassifier androidBuildClassifier, Context context) {
CompatConfig config = new CompatConfig(androidBuildClassifier, context);
- config.initConfigFromLib(Environment.buildPath(
+ config.loadConfigFiles();
+ config.initOverrides();
+ config.invalidateCache();
+ return config;
+ }
+
+ @android.ravenwood.annotation.RavenwoodReplace
+ private void loadConfigFiles() {
+ initConfigFromLib(Environment.buildPath(
Environment.getRootDirectory(), "etc", "compatconfig"));
- config.initConfigFromLib(Environment.buildPath(
+ initConfigFromLib(Environment.buildPath(
Environment.getRootDirectory(), "system_ext", "etc", "compatconfig"));
List<ApexManager.ActiveApexInfo> apexes = ApexManager.getInstance().getActiveApexInfos();
for (ApexManager.ActiveApexInfo apex : apexes) {
- config.initConfigFromLib(Environment.buildPath(
+ initConfigFromLib(Environment.buildPath(
apex.apexDirectory, "etc", "compatconfig"));
}
- config.initOverrides();
- config.invalidateCache();
- return config;
+ }
+
+ @SuppressWarnings("unused")
+ private void loadConfigFiles$ravenwood() {
+ final var configDir = new File(
+ RavenwoodEnvironment.getInstance().getRavenwoodRuntimePath()
+ + APP_COMPAT_DATA_DIR_RAVENWOOD);
+ initConfigFromLib(configDir, (file) -> file.getName().endsWith(OVERRIDES_FILE_RAVENWOOD));
}
/**
@@ -678,12 +697,25 @@
return changeInfos;
}
+ /**
+ * Load all config files in a given directory.
+ */
void initConfigFromLib(File libraryDir) {
+ initConfigFromLib(libraryDir, (file) -> true);
+ }
+
+ /**
+ * Load config files in a given directory, but only the ones that match {@code includingFilter}.
+ */
+ void initConfigFromLib(File libraryDir, Predicate<File> includingFilter) {
if (!libraryDir.exists() || !libraryDir.isDirectory()) {
Slog.d(TAG, "No directory " + libraryDir + ", skipping");
return;
}
for (File f : libraryDir.listFiles()) {
+ if (!includingFilter.test(f)) {
+ continue;
+ }
Slog.d(TAG, "Found a config file: " + f.getPath());
//TODO(b/138222363): Handle duplicate ids across config files.
readConfig(f);
diff --git a/services/core/java/com/android/server/compat/OverrideValidatorImpl.java b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java
index e3b6d03..362c697 100644
--- a/services/core/java/com/android/server/compat/OverrideValidatorImpl.java
+++ b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java
@@ -45,6 +45,7 @@
/**
* Implementation of the policy for allowing compat change overrides.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class OverrideValidatorImpl extends IOverrideValidator.Stub {
private AndroidBuildClassifier mAndroidBuildClassifier;
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index df49aff..2186e2a 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -36,6 +36,7 @@
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
+import android.os.PermissionEnforcer;
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -65,6 +66,7 @@
/**
* System server internal API for gating and reporting compatibility changes.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class PlatformCompat extends IPlatformCompat.Stub {
private static final String TAG = "Compatibility";
@@ -75,6 +77,7 @@
private final AndroidBuildClassifier mBuildClassifier;
public PlatformCompat(Context context) {
+ super(PermissionEnforcer.fromContext(context));
mContext = context;
mChangeReporter = new ChangeReporter(ChangeReporter.SOURCE_SYSTEM_SERVER);
mBuildClassifier = new AndroidBuildClassifier();
@@ -84,6 +87,7 @@
@VisibleForTesting
PlatformCompat(Context context, CompatConfig compatConfig,
AndroidBuildClassifier buildClassifier) {
+ super(PermissionEnforcer.fromContext(context));
mContext = context;
mChangeReporter = new ChangeReporter(ChangeReporter.SOURCE_SYSTEM_SERVER);
mCompatConfig = compatConfig;
@@ -492,6 +496,7 @@
packageName, 0, Process.myUid(), userId);
}
+ @android.ravenwood.annotation.RavenwoodReplace
private void killPackage(String packageName) {
int uid = LocalServices.getService(PackageManagerInternal.class).getPackageUid(packageName,
0, UserHandle.myUserId());
@@ -505,6 +510,13 @@
killUid(UserHandle.getAppId(uid));
}
+ @SuppressWarnings("unused")
+ private void killPackage$ravenwood(String packageName) {
+ // TODO Maybe crash if the package is the self.
+ Slog.w(TAG, "killPackage() is ignored on Ravenwood: packageName=" + packageName);
+ }
+
+ @android.ravenwood.annotation.RavenwoodReplace
private void killUid(int appId) {
final long identity = Binder.clearCallingIdentity();
try {
@@ -519,6 +531,12 @@
}
}
+ @SuppressWarnings("unused")
+ private void killUid$ravenwood(int appId) {
+ // TODO Maybe crash if the UID is the self.
+ Slog.w(TAG, "killUid() is ignored on Ravenwood: appId=" + appId);
+ }
+
private void checkAllCompatOverridesAreOverridable(Collection<Long> changeIds) {
for (Long changeId : changeIds) {
if (isKnownChangeId(changeId) && !mCompatConfig.isOverridable(changeId)) {
diff --git a/services/core/java/com/android/server/compat/PlatformCompatNative.java b/services/core/java/com/android/server/compat/PlatformCompatNative.java
index 5d7af65..7a3feb5 100644
--- a/services/core/java/com/android/server/compat/PlatformCompatNative.java
+++ b/services/core/java/com/android/server/compat/PlatformCompatNative.java
@@ -23,6 +23,7 @@
/**
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class PlatformCompatNative extends IPlatformCompatNative.Stub {
private final PlatformCompat mPlatformCompat;
diff --git a/services/core/java/com/android/server/compat/overrides/AppCompatOverridesParser.java b/services/core/java/com/android/server/compat/overrides/AppCompatOverridesParser.java
index e8762a3..0ec6879 100644
--- a/services/core/java/com/android/server/compat/overrides/AppCompatOverridesParser.java
+++ b/services/core/java/com/android/server/compat/overrides/AppCompatOverridesParser.java
@@ -46,6 +46,7 @@
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
final class AppCompatOverridesParser {
/**
* Flag for specifying all compat change IDs owned by a namespace. See {@link
diff --git a/services/core/java/com/android/server/compat/overrides/AppCompatOverridesService.java b/services/core/java/com/android/server/compat/overrides/AppCompatOverridesService.java
index fe002ce..8637d2d 100644
--- a/services/core/java/com/android/server/compat/overrides/AppCompatOverridesService.java
+++ b/services/core/java/com/android/server/compat/overrides/AppCompatOverridesService.java
@@ -68,6 +68,7 @@
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class AppCompatOverridesService {
private static final String TAG = "AppCompatOverridesService";
diff --git a/services/core/java/com/android/server/display/color/TintController.java b/services/core/java/com/android/server/display/color/TintController.java
index 716661d..68dc80f 100644
--- a/services/core/java/com/android/server/display/color/TintController.java
+++ b/services/core/java/com/android/server/display/color/TintController.java
@@ -19,6 +19,7 @@
import android.animation.ValueAnimator;
import android.content.Context;
import android.util.Slog;
+import com.android.internal.annotations.GuardedBy;
import java.io.PrintWriter;
@@ -29,23 +30,33 @@
*/
private static final long TRANSITION_DURATION = 3000L;
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
private ValueAnimator mAnimator;
+ @GuardedBy("mLock")
private Boolean mIsActivated;
public ValueAnimator getAnimator() {
- return mAnimator;
+ synchronized (mLock) {
+ return mAnimator;
+ }
}
public void setAnimator(ValueAnimator animator) {
- mAnimator = animator;
+ synchronized (mLock) {
+ mAnimator = animator;
+ }
}
/**
* Cancel the animator if it's still running.
*/
public void cancelAnimator() {
- if (mAnimator != null) {
- mAnimator.cancel();
+ synchronized (mLock) {
+ if (mAnimator != null) {
+ mAnimator.cancel();
+ }
}
}
@@ -53,22 +64,30 @@
* End the animator if it's still running, jumping to the end state.
*/
public void endAnimator() {
- if (mAnimator != null) {
- mAnimator.end();
- mAnimator = null;
+ synchronized (mLock) {
+ if (mAnimator != null) {
+ mAnimator.end();
+ mAnimator = null;
+ }
}
}
public void setActivated(Boolean isActivated) {
- mIsActivated = isActivated;
+ synchronized (mLock) {
+ mIsActivated = isActivated;
+ }
}
public boolean isActivated() {
- return mIsActivated != null && mIsActivated;
+ synchronized (mLock) {
+ return mIsActivated != null && mIsActivated;
+ }
}
public boolean isActivatedStateNotSet() {
- return mIsActivated == null;
+ synchronized (mLock) {
+ return mIsActivated == null;
+ }
}
public long getTransitionDurationMilliseconds() {
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 89ddb3f..a17de65 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -824,7 +824,8 @@
if (windowingMode > -1) {
if (mService.isInLockTaskMode()
- && WindowConfiguration.inMultiWindowMode(windowingMode)) {
+ && WindowConfiguration.inMultiWindowMode(windowingMode)
+ && !container.isEmbedded()) {
Slog.w(TAG, "Dropping unsupported request to set multi-window windowing mode"
+ " during locked task mode.");
return effects;
diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
index 4e86888..e307e52 100644
--- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
+++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
@@ -144,6 +144,10 @@
public void onBootPhase(int phase) {
if (phase == PHASE_SYSTEM_SERVICES_READY) {
UsbManager usbManager = getContext().getSystemService(UsbManager.class);
+ if (usbManager == null) {
+ mAdbActive = false;
+ return;
+ }
mAdbActive = ((usbManager.getCurrentFunctions() & UsbManager.FUNCTION_ADB) == 1);
Log.d(LOG_TAG, "ADB is " + mAdbActive + " on system startup");
}
diff --git a/services/tests/VpnTests/java/com/android/server/connectivity/VpnTest.java b/services/tests/VpnTests/java/com/android/server/connectivity/VpnTest.java
index 08155c7..9772ef9 100644
--- a/services/tests/VpnTests/java/com/android/server/connectivity/VpnTest.java
+++ b/services/tests/VpnTests/java/com/android/server/connectivity/VpnTest.java
@@ -2380,10 +2380,14 @@
@Test
public void doTestMigrateIkeSession_Vcn() throws Exception {
final int expectedKeepalive = 2097; // Any unlikely number will do
- final NetworkCapabilities vcnNc = new NetworkCapabilities.Builder()
- .addTransportType(TRANSPORT_CELLULAR)
- .setTransportInfo(new VcnTransportInfo(TEST_SUB_ID, expectedKeepalive))
- .build();
+ final NetworkCapabilities vcnNc =
+ new NetworkCapabilities.Builder()
+ .addTransportType(TRANSPORT_CELLULAR)
+ .setTransportInfo(
+ new VcnTransportInfo.Builder()
+ .setMinUdpPort4500NatTimeoutSeconds(expectedKeepalive)
+ .build())
+ .build();
final Ikev2VpnProfile ikev2VpnProfile = makeIkeV2VpnProfile(
true /* isAutomaticIpVersionSelectionEnabled */,
true /* isAutomaticNattKeepaliveTimerEnabled */,
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index d336c99..c098868 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -149,21 +149,49 @@
resource_zips: [":FrameworksServicesTests_apks_as_resources"],
}
-android_ravenwood_test {
- name: "FrameworksServicesTestsRavenwood",
+java_defaults {
+ name: "FrameworksServicesTestsRavenwood-defaults",
libs: [
"android.test.mock.stubs.system",
],
static_libs: [
"androidx.annotation_annotation",
"androidx.test.rules",
- "services.core",
"flag-junit",
],
+ auto_gen_config: true,
+}
+
+// Unit tests for UriGrantManager, running on ravenwood.
+// Note UriGrantManager does not support Ravenwood (yet). We're just running the original
+// unit tests as is on Ravenwood. So here, we use the original "services.core", because
+// "services.core.ravenwood" doesn't have the target code.
+// (Compare to FrameworksServicesTestsRavenwood_Compat, which does support Ravenwood.)
+android_ravenwood_test {
+ name: "FrameworksServicesTestsRavenwood_Uri",
+ defaults: ["FrameworksServicesTestsRavenwood-defaults"],
+ team: "trendy_team_ravenwood",
+ static_libs: [
+ "services.core",
+ ],
srcs: [
"src/com/android/server/uri/**/*.java",
],
- auto_gen_config: true,
+}
+
+// Unit tests for compat-framework.
+// Compat-framework does support Ravenwood, and it uses the ravenwood anottations,
+// so we link "services.core.ravenwood".
+android_ravenwood_test {
+ name: "FrameworksServicesTestsRavenwood_Compat",
+ defaults: ["FrameworksServicesTestsRavenwood-defaults"],
+ team: "trendy_team_ravenwood",
+ static_libs: [
+ "services.core.ravenwood",
+ ],
+ srcs: [
+ "src/com/android/server/compat/**/*.java",
+ ],
}
java_library {
diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
index 36b163e..3d695a6 100644
--- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
@@ -18,12 +18,12 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
-import static org.testng.Assert.assertThrows;
import android.app.compat.ChangeIdStateCache;
import android.app.compat.PackageOverride;
diff --git a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
index 9accd49..79f0673 100644
--- a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
@@ -18,6 +18,7 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.never;
@@ -25,13 +26,15 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.internal.verification.VerificationModeFactory.times;
-import static org.testng.Assert.assertThrows;
import android.compat.Compatibility.ChangeConfig;
+import android.content.AttributionSource;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.os.Build;
+import android.os.PermissionEnforcer;
+import android.permission.PermissionCheckerManager;
import androidx.test.runner.AndroidJUnit4;
@@ -77,6 +80,22 @@
.thenReturn(-1);
when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
.thenThrow(new PackageManager.NameNotFoundException());
+
+ var allGrantingPermissionEnforcer = new PermissionEnforcer() {
+ @Override
+ protected int checkPermission(String permission, AttributionSource source) {
+ return PermissionCheckerManager.PERMISSION_GRANTED;
+ }
+
+ @Override
+ protected int checkPermission(String permission, int pid, int uid) {
+ return PermissionCheckerManager.PERMISSION_GRANTED;
+ }
+ };
+
+ when(mContext.getSystemService(eq(Context.PERMISSION_ENFORCER_SERVICE)))
+ .thenReturn(allGrantingPermissionEnforcer);
+
mCompatConfig = new CompatConfig(mBuildClassifier, mContext);
mPlatformCompat = new PlatformCompat(mContext, mCompatConfig, mBuildClassifier);
// Assume userdebug/eng non-final build
diff --git a/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java
index 24abc18..f549453 100644
--- a/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java
@@ -61,7 +61,6 @@
import android.os.UserHandle;
import android.platform.test.flag.junit.FlagsParameterization;
import android.platform.test.flag.junit.SetFlagsRule;
-import android.platform.test.ravenwood.RavenwoodRule;
import android.util.ArraySet;
import org.junit.Before;
@@ -77,9 +76,6 @@
@RunWith(Parameterized.class)
public class UriGrantsManagerServiceTest {
- @Rule
- public final RavenwoodRule mRavenwood = new RavenwoodRule();
-
/**
* Why this class needs to test all combinations of
* {@link android.security.Flags#FLAG_CONTENT_URI_PERMISSION_APIS}:
diff --git a/services/tests/servicestests/src/com/android/server/uri/UriPermissionTest.java b/services/tests/servicestests/src/com/android/server/uri/UriPermissionTest.java
index 611c514..fe66f73 100644
--- a/services/tests/servicestests/src/com/android/server/uri/UriPermissionTest.java
+++ b/services/tests/servicestests/src/com/android/server/uri/UriPermissionTest.java
@@ -37,18 +37,13 @@
import static org.junit.Assert.assertTrue;
import android.os.SystemClock;
-import android.platform.test.ravenwood.RavenwoodRule;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
public class UriPermissionTest {
- @Rule
- public final RavenwoodRule mRavenwood = new RavenwoodRule();
-
@Mock
private UriGrantsManagerInternal mService;