Merge "[HostStubGen] Use buffered output" into main
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 8dc9652..0db91f5 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2579,9 +2579,10 @@
 
   @FlaggedApi("android.os.vibrator.vendor_vibration_effects") public static final class VibrationEffect.VendorEffect extends android.os.VibrationEffect {
     method @Nullable public long[] computeCreateWaveformOffOnTimingsOrNull();
+    method public float getAdaptiveScale();
     method public long getDuration();
     method public int getEffectStrength();
-    method public float getLinearScale();
+    method public float getScale();
     method @NonNull public android.os.PersistableBundle getVendorData();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.os.VibrationEffect.VendorEffect> CREATOR;
diff --git a/core/java/android/app/appfunctions/ServiceCallHelper.java b/core/java/android/app/appfunctions/ServiceCallHelper.java
new file mode 100644
index 0000000..cc882bd
--- /dev/null
+++ b/core/java/android/app/appfunctions/ServiceCallHelper.java
@@ -0,0 +1,86 @@
+/*
+ * 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.app.appfunctions;
+
+import android.annotation.NonNull;
+import android.content.Intent;
+import android.os.UserHandle;
+
+/**
+ * Defines a contract for establishing temporary connections to services and executing operations
+ * within a specified timeout. Implementations of this interface provide mechanisms to ensure that
+ * services are properly unbound after the operation completes or a timeout occurs.
+ *
+ * @param <T> Class of wrapped service.
+ * @hide
+ */
+public interface ServiceCallHelper<T> {
+
+    /**
+     * Initiates service binding and executes a provided method when the service connects. Unbinds
+     * the service after execution or upon timeout. Returns the result of the bindService API.
+     *
+     * <p>When the service connection was made successfully, it's the caller responsibility to
+     * report the usage is completed and can be unbound by calling {@link
+     * ServiceUsageCompleteListener#onCompleted()}.
+     *
+     * <p>This method includes a timeout mechanism to prevent the system from being stuck in a state
+     * where a service is bound indefinitely (for example, if the binder method never returns). This
+     * helps ensure that the calling app does not remain alive unnecessarily.
+     *
+     * @param intent An Intent object that describes the service that should be bound.
+     * @param bindFlags Flags used to control the binding process See {@link
+     *     android.content.Context#bindService}.
+     * @param timeoutInMillis The maximum time in milliseconds to wait for the service connection.
+     * @param userHandle The UserHandle of the user for which the service should be bound.
+     * @param callback A callback to be invoked for various events. See {@link
+     *     RunServiceCallCallback}.
+     */
+    boolean runServiceCall(
+            @NonNull Intent intent,
+            int bindFlags,
+            long timeoutInMillis,
+            @NonNull UserHandle userHandle,
+            @NonNull RunServiceCallCallback<T> callback);
+
+    /** An interface for clients to signal that they have finished using a bound service. */
+    interface ServiceUsageCompleteListener {
+        /**
+         * Called when a client has finished using a bound service. This indicates that the service
+         * can be safely unbound.
+         */
+        void onCompleted();
+    }
+
+    interface RunServiceCallCallback<T> {
+        /**
+         * Called when the service connection has been established. Uses {@code
+         * serviceUsageCompleteListener} to report finish using the connected service.
+         */
+        void onServiceConnected(
+                @NonNull T service,
+                @NonNull ServiceUsageCompleteListener serviceUsageCompleteListener);
+
+        /** Called when the service connection was failed to establish. */
+        void onFailedToConnect();
+
+        /**
+         * Called when the whole operation(i.e. binding and the service call) takes longer than
+         * allowed.
+         */
+        void onTimedOut();
+    }
+}
diff --git a/core/java/android/app/appfunctions/ServiceCallHelperImpl.java b/core/java/android/app/appfunctions/ServiceCallHelperImpl.java
new file mode 100644
index 0000000..2e58546
--- /dev/null
+++ b/core/java/android/app/appfunctions/ServiceCallHelperImpl.java
@@ -0,0 +1,157 @@
+/*
+ * 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.app.appfunctions;
+
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.util.Log;
+
+import java.util.concurrent.Executor;
+import java.util.function.Function;
+
+/**
+ * An implementation of {@link android.app.appfunctions.ServiceCallHelper} that that is based on
+ * {@link Context#bindService}.
+ *
+ * @param <T> Class of wrapped service.
+ * @hide
+ */
+public class ServiceCallHelperImpl<T> implements ServiceCallHelper<T> {
+    private static final String TAG = "AppFunctionsServiceCall";
+
+    @NonNull private final Context mContext;
+    @NonNull private final Function<IBinder, T> mInterfaceConverter;
+    private final Handler mHandler = new Handler(Looper.getMainLooper());
+    private final Executor mExecutor;
+
+    /**
+     * @param interfaceConverter A function responsible for converting an IBinder object into the
+     *     desired service interface.
+     * @param executor An Executor instance to dispatch callback.
+     * @param context The system context.
+     */
+    public ServiceCallHelperImpl(
+            @NonNull Context context,
+            @NonNull Function<IBinder, T> interfaceConverter,
+            @NonNull Executor executor) {
+        mContext = context;
+        mInterfaceConverter = interfaceConverter;
+        mExecutor = executor;
+    }
+
+    @Override
+    public boolean runServiceCall(
+            @NonNull Intent intent,
+            int bindFlags,
+            long timeoutInMillis,
+            @NonNull UserHandle userHandle,
+            @NonNull RunServiceCallCallback<T> callback) {
+        OneOffServiceConnection serviceConnection =
+                new OneOffServiceConnection(
+                        intent, bindFlags, timeoutInMillis, userHandle, callback);
+
+        return serviceConnection.bindAndRun();
+    }
+
+    private class OneOffServiceConnection
+            implements ServiceConnection, ServiceUsageCompleteListener {
+        private final Intent mIntent;
+        private final int mFlags;
+        private final long mTimeoutMillis;
+        private final UserHandle mUserHandle;
+        private final RunServiceCallCallback<T> mCallback;
+        private final Runnable mTimeoutCallback;
+
+        OneOffServiceConnection(
+                @NonNull Intent intent,
+                int flags,
+                long timeoutMillis,
+                @NonNull UserHandle userHandle,
+                @NonNull RunServiceCallCallback<T> callback) {
+            mIntent = intent;
+            mFlags = flags;
+            mTimeoutMillis = timeoutMillis;
+            mCallback = callback;
+            mTimeoutCallback =
+                    () ->
+                            mExecutor.execute(
+                                    () -> {
+                                        safeUnbind();
+                                        mCallback.onTimedOut();
+                                    });
+            mUserHandle = userHandle;
+        }
+
+        public boolean bindAndRun() {
+            boolean bindServiceResult =
+                    mContext.bindServiceAsUser(mIntent, this, mFlags, mUserHandle);
+
+            if (bindServiceResult) {
+                mHandler.postDelayed(mTimeoutCallback, mTimeoutMillis);
+            } else {
+                safeUnbind();
+            }
+
+            return bindServiceResult;
+        }
+
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            T serviceInterface = mInterfaceConverter.apply(service);
+
+            mExecutor.execute(() -> mCallback.onServiceConnected(serviceInterface, this));
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            safeUnbind();
+            mExecutor.execute(mCallback::onFailedToConnect);
+        }
+
+        @Override
+        public void onBindingDied(ComponentName name) {
+            safeUnbind();
+            mExecutor.execute(mCallback::onFailedToConnect);
+        }
+
+        @Override
+        public void onNullBinding(ComponentName name) {
+            safeUnbind();
+            mExecutor.execute(mCallback::onFailedToConnect);
+        }
+
+        private void safeUnbind() {
+            try {
+                mHandler.removeCallbacks(mTimeoutCallback);
+                mContext.unbindService(this);
+            } catch (Exception ex) {
+                Log.w(TAG, "Failed to unbind", ex);
+            }
+        }
+
+        @Override
+        public void onCompleted() {
+            safeUnbind();
+        }
+    }
+}
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index e68b746..f02d4a9 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -346,7 +346,7 @@
     @RequiresPermission(android.Manifest.permission.VIBRATE_VENDOR_EFFECTS)
     public static VibrationEffect createVendorEffect(@NonNull PersistableBundle effect) {
         VibrationEffect vendorEffect = new VendorEffect(effect, VendorEffect.DEFAULT_STRENGTH,
-                VendorEffect.DEFAULT_SCALE);
+                VendorEffect.DEFAULT_SCALE, VendorEffect.DEFAULT_SCALE);
         vendorEffect.validate();
         return vendorEffect;
     }
@@ -623,7 +623,7 @@
      * @hide
      */
     @NonNull
-    public abstract VibrationEffect scaleLinearly(float scaleFactor);
+    public abstract VibrationEffect applyAdaptiveScale(float scaleFactor);
 
     /**
      * Ensures that the effect is repeating indefinitely or not. This is a lossy operation and
@@ -948,7 +948,7 @@
         /** @hide */
         @NonNull
         @Override
-        public Composed scaleLinearly(float scaleFactor) {
+        public Composed applyAdaptiveScale(float scaleFactor) {
             return applyToSegments(VibrationEffectSegment::scaleLinearly, scaleFactor);
         }
 
@@ -1100,21 +1100,23 @@
 
         private final PersistableBundle mVendorData;
         private final int mEffectStrength;
-        private final float mLinearScale;
+        private final float mScale;
+        private final float mAdaptiveScale;
 
         /** @hide */
         VendorEffect(@NonNull Parcel in) {
             this(Objects.requireNonNull(
                     in.readPersistableBundle(VibrationEffect.class.getClassLoader())),
-                    in.readInt(), in.readFloat());
+                    in.readInt(), in.readFloat(), in.readFloat());
         }
 
         /** @hide */
         public VendorEffect(@NonNull PersistableBundle vendorData, int effectStrength,
-                float linearScale) {
+                float scale, float adaptiveScale) {
             mVendorData = vendorData;
             mEffectStrength = effectStrength;
-            mLinearScale = linearScale;
+            mScale = scale;
+            mAdaptiveScale = adaptiveScale;
         }
 
         @NonNull
@@ -1126,8 +1128,12 @@
             return mEffectStrength;
         }
 
-        public float getLinearScale() {
-            return mLinearScale;
+        public float getScale() {
+            return mScale;
+        }
+
+        public float getAdaptiveScale() {
+            return mAdaptiveScale;
         }
 
         /** @hide */
@@ -1175,7 +1181,8 @@
             if (mEffectStrength == effectStrength) {
                 return this;
             }
-            VendorEffect updated = new VendorEffect(mVendorData, effectStrength, mLinearScale);
+            VendorEffect updated = new VendorEffect(mVendorData, effectStrength, mScale,
+                    mAdaptiveScale);
             updated.validate();
             return updated;
         }
@@ -1184,18 +1191,24 @@
         @NonNull
         @Override
         public VendorEffect scale(float scaleFactor) {
-            // Vendor effect strength cannot be scaled with this method.
-            return this;
+            if (Float.compare(mScale, scaleFactor) == 0) {
+                return this;
+            }
+            VendorEffect updated = new VendorEffect(mVendorData, mEffectStrength, scaleFactor,
+                    mAdaptiveScale);
+            updated.validate();
+            return updated;
         }
 
         /** @hide */
         @NonNull
         @Override
-        public VibrationEffect scaleLinearly(float scaleFactor) {
-            if (Float.compare(mLinearScale, scaleFactor) == 0) {
+        public VibrationEffect applyAdaptiveScale(float scaleFactor) {
+            if (Float.compare(mAdaptiveScale, scaleFactor) == 0) {
                 return this;
             }
-            VendorEffect updated = new VendorEffect(mVendorData, mEffectStrength, scaleFactor);
+            VendorEffect updated = new VendorEffect(mVendorData, mEffectStrength, mScale,
+                    scaleFactor);
             updated.validate();
             return updated;
         }
@@ -1216,29 +1229,31 @@
                 return false;
             }
             return mEffectStrength == other.mEffectStrength
-                    && (Float.compare(mLinearScale, other.mLinearScale) == 0)
+                    && (Float.compare(mScale, other.mScale) == 0)
+                    && (Float.compare(mAdaptiveScale, other.mAdaptiveScale) == 0)
                     && isPersistableBundleEquals(mVendorData, other.mVendorData);
         }
 
         @Override
         public int hashCode() {
             // PersistableBundle does not implement hashCode, so use its size as a shortcut.
-            return Objects.hash(mVendorData.size(), mEffectStrength, mLinearScale);
+            return Objects.hash(mVendorData.size(), mEffectStrength, mScale, mAdaptiveScale);
         }
 
         @Override
         public String toString() {
             return String.format(Locale.ROOT,
-                    "VendorEffect{vendorData=%s, strength=%s, scale=%.2f}",
-                    mVendorData, effectStrengthToString(mEffectStrength), mLinearScale);
+                    "VendorEffect{vendorData=%s, strength=%s, scale=%.2f, adaptiveScale=%.2f}",
+                    mVendorData, effectStrengthToString(mEffectStrength), mScale, mAdaptiveScale);
         }
 
         /** @hide */
         @Override
         public String toDebugString() {
-            return String.format(Locale.ROOT, "vendorEffect=%s, strength=%s, scale=%.2f",
+            return String.format(Locale.ROOT,
+                    "vendorEffect=%s, strength=%s, scale=%.2f, adaptiveScale=%.2f",
                     mVendorData.toShortString(), effectStrengthToString(mEffectStrength),
-                    mLinearScale);
+                    mScale, mAdaptiveScale);
         }
 
         @Override
@@ -1251,7 +1266,8 @@
             out.writeInt(PARCEL_TOKEN_VENDOR_EFFECT);
             out.writePersistableBundle(mVendorData);
             out.writeInt(mEffectStrength);
-            out.writeFloat(mLinearScale);
+            out.writeFloat(mScale);
+            out.writeFloat(mAdaptiveScale);
         }
 
         /**
diff --git a/core/java/android/text/Selection.java b/core/java/android/text/Selection.java
index 6a54d23..711578c 100644
--- a/core/java/android/text/Selection.java
+++ b/core/java/android/text/Selection.java
@@ -350,7 +350,7 @@
     private static final char PARAGRAPH_SEPARATOR = '\n';
 
     /**
-     * Move the cusrot to the closest paragraph start offset.
+     * Move the cursor to the closest paragraph start offset.
      *
      * @param text the spannable text
      * @param layout layout to be used for drawing.
diff --git a/core/tests/resourceflaggingtests/src/com/android/resourceflaggingtests/ResourceFlaggingTest.java b/core/tests/resourceflaggingtests/src/com/android/resourceflaggingtests/ResourceFlaggingTest.java
index c1e3578..471b402 100644
--- a/core/tests/resourceflaggingtests/src/com/android/resourceflaggingtests/ResourceFlaggingTest.java
+++ b/core/tests/resourceflaggingtests/src/com/android/resourceflaggingtests/ResourceFlaggingTest.java
@@ -68,6 +68,16 @@
         assertThat(getBoolean("res3")).isTrue();
     }
 
+    @Test
+    public void testFlagDisabledStringArrayElement() {
+        assertThat(getStringArray("strarr1")).isEqualTo(new String[]{"one", "two", "three"});
+    }
+
+    @Test
+    public void testFlagDisabledIntArrayElement() {
+        assertThat(getIntArray("intarr1")).isEqualTo(new int[]{1, 2, 3});
+    }
+
     private boolean getBoolean(String name) {
         int resId = mResources.getIdentifier(
                 name,
@@ -77,13 +87,22 @@
         return mResources.getBoolean(resId);
     }
 
-    private String getString(String name) {
+    private String[] getStringArray(String name) {
         int resId = mResources.getIdentifier(
                 name,
-                "string",
+                "array",
                 "com.android.intenal.flaggedresources");
         assertThat(resId).isNotEqualTo(0);
-        return mResources.getString(resId);
+        return mResources.getStringArray(resId);
+    }
+
+    private int[] getIntArray(String name) {
+        int resId = mResources.getIdentifier(
+                name,
+                "array",
+                "com.android.intenal.flaggedresources");
+        assertThat(resId).isNotEqualTo(0);
+        return mResources.getIntArray(resId);
     }
 
     private String extractApkAndGetPath(int id) throws Exception {
diff --git a/core/tests/vibrator/src/android/os/VibrationEffectTest.java b/core/tests/vibrator/src/android/os/VibrationEffectTest.java
index bd3d944..4f76dd6 100644
--- a/core/tests/vibrator/src/android/os/VibrationEffectTest.java
+++ b/core/tests/vibrator/src/android/os/VibrationEffectTest.java
@@ -60,7 +60,7 @@
 
 @RunWith(MockitoJUnitRunner.class)
 public class VibrationEffectTest {
-
+    private static final float TOLERANCE = 1e-2f;
     private static final String RINGTONE_URI_1 = "content://test/system/ringtone_1";
     private static final String RINGTONE_URI_2 = "content://test/system/ringtone_2";
     private static final String RINGTONE_URI_3 = "content://test/system/ringtone_3";
@@ -709,7 +709,7 @@
     @Test
     public void testScaleWaveform() {
         VibrationEffect scaledUp = TEST_WAVEFORM.scale(1.5f);
-        assertEquals(1f, getStepSegment(scaledUp, 0).getAmplitude(), 1e-5f);
+        assertEquals(1f, getStepSegment(scaledUp, 0).getAmplitude(), TOLERANCE);
 
         VibrationEffect scaledDown = TEST_WAVEFORM.scale(0.5f);
         assertTrue(1f > getStepSegment(scaledDown, 0).getAmplitude());
@@ -731,11 +731,11 @@
     public void testScaleVendorEffect() {
         VibrationEffect effect = VibrationEffect.createVendorEffect(createNonEmptyBundle());
 
-        VibrationEffect scaledUp = effect.scale(1.5f);
-        assertEquals(effect, scaledUp);
+        VibrationEffect.VendorEffect scaledUp = (VibrationEffect.VendorEffect) effect.scale(1.5f);
+        assertEquals(1.5f, scaledUp.getScale());
 
-        VibrationEffect scaledDown = effect.scale(0.5f);
-        assertEquals(effect, scaledDown);
+        VibrationEffect.VendorEffect scaledDown = (VibrationEffect.VendorEffect) effect.scale(0.5f);
+        assertEquals(0.5f, scaledDown.getScale());
     }
 
     @Test
@@ -755,6 +755,70 @@
     }
 
     @Test
+    public void testApplyAdaptiveScaleOneShot() {
+        VibrationEffect oneShot = VibrationEffect.createOneShot(TEST_TIMING, /* amplitude= */ 100);
+
+        VibrationEffect scaledUp = oneShot.applyAdaptiveScale(1.5f);
+        assertThat(getStepSegment(scaledUp, 0).getAmplitude()).isWithin(TOLERANCE).of(150 / 255f);
+
+        VibrationEffect scaledDown = oneShot.applyAdaptiveScale(0.5f);
+        assertThat(getStepSegment(scaledDown, 0).getAmplitude()).isWithin(TOLERANCE).of(50 / 255f);
+    }
+
+    @Test
+    public void testApplyAdaptiveScaleWaveform() {
+        VibrationEffect waveform = VibrationEffect.createWaveform(
+                new long[] { 100, 100 }, new int[] { 10, 0 }, -1);
+
+        VibrationEffect scaledUp = waveform.applyAdaptiveScale(1.5f);
+        assertThat(getStepSegment(scaledUp, 0).getAmplitude()).isWithin(TOLERANCE).of(15 / 255f);
+
+        VibrationEffect scaledDown = waveform.applyAdaptiveScale(0.5f);
+        assertThat(getStepSegment(scaledDown, 0).getAmplitude()).isWithin(TOLERANCE).of(5 / 255f);
+    }
+
+    @Test
+    public void testApplyAdaptiveScalePrebaked() {
+        VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
+
+        VibrationEffect scaledUp = effect.applyAdaptiveScale(1.5f);
+        assertEquals(effect, scaledUp);
+
+        VibrationEffect scaledDown = effect.applyAdaptiveScale(0.5f);
+        assertEquals(effect, scaledDown);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+    public void testApplyAdaptiveScaleVendorEffect() {
+        VibrationEffect effect = VibrationEffect.createVendorEffect(createNonEmptyBundle());
+
+        VibrationEffect.VendorEffect scaledUp =
+                (VibrationEffect.VendorEffect) effect.applyAdaptiveScale(1.5f);
+        assertEquals(1.5f, scaledUp.getAdaptiveScale());
+
+        VibrationEffect.VendorEffect scaledDown =
+                (VibrationEffect.VendorEffect) effect.applyAdaptiveScale(0.5f);
+        assertEquals(0.5f, scaledDown.getAdaptiveScale());
+    }
+
+    @Test
+    public void testApplyAdaptiveScaleComposed() {
+        VibrationEffect effect = VibrationEffect.startComposition()
+                .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 0.5f, 1)
+                .addEffect(VibrationEffect.createOneShot(TEST_TIMING, /* amplitude= */ 100))
+                .compose();
+
+        VibrationEffect scaledUp = effect.applyAdaptiveScale(1.5f);
+        assertThat(getPrimitiveSegment(scaledUp, 0).getScale()).isWithin(TOLERANCE).of(0.75f);
+        assertThat(getStepSegment(scaledUp, 1).getAmplitude()).isWithin(TOLERANCE).of(150 / 255f);
+
+        VibrationEffect scaledDown = effect.applyAdaptiveScale(0.5f);
+        assertThat(getPrimitiveSegment(scaledDown, 0).getScale()).isWithin(TOLERANCE).of(0.25f);
+        assertThat(getStepSegment(scaledDown, 1).getAmplitude()).isWithin(TOLERANCE).of(50 / 255f);
+    }
+
+    @Test
     public void testApplyEffectStrengthToOneShotWaveformAndPrimitives() {
         VibrationEffect oneShot = VibrationEffect.createOneShot(100, 100);
         VibrationEffect waveform = VibrationEffect.createWaveform(new long[] { 10, 20 }, 0);
diff --git a/nfc/api/current.txt b/nfc/api/current.txt
index b0d1f71..447e980 100644
--- a/nfc/api/current.txt
+++ b/nfc/api/current.txt
@@ -220,11 +220,14 @@
     field @Deprecated public static final String ACTION_CHANGE_DEFAULT = "android.nfc.cardemulation.action.ACTION_CHANGE_DEFAULT";
     field public static final String CATEGORY_OTHER = "other";
     field public static final String CATEGORY_PAYMENT = "payment";
+    field @FlaggedApi("android.nfc.nfc_override_recover_routing_table") public static final String DH = "DH";
+    field @FlaggedApi("android.nfc.nfc_override_recover_routing_table") public static final String ESE = "ESE";
     field public static final String EXTRA_CATEGORY = "category";
     field public static final String EXTRA_SERVICE_COMPONENT = "component";
     field public static final int SELECTION_MODE_ALWAYS_ASK = 1; // 0x1
     field public static final int SELECTION_MODE_ASK_IF_CONFLICT = 2; // 0x2
     field public static final int SELECTION_MODE_PREFER_DEFAULT = 0; // 0x0
+    field @FlaggedApi("android.nfc.nfc_override_recover_routing_table") public static final String UICC = "UICC";
   }
 
   public abstract class HostApduService extends android.app.Service {
diff --git a/nfc/api/system-current.txt b/nfc/api/system-current.txt
index ae63e19..2ff9829 100644
--- a/nfc/api/system-current.txt
+++ b/nfc/api/system-current.txt
@@ -75,6 +75,8 @@
   public final class CardEmulation {
     method @FlaggedApi("android.permission.flags.wallet_role_enabled") @Nullable @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public static android.content.ComponentName getPreferredPaymentService(@NonNull android.content.Context);
     method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public java.util.List<android.nfc.cardemulation.ApduServiceInfo> getServices(@NonNull String, int);
+    method @FlaggedApi("android.nfc.nfc_override_recover_routing_table") public void overrideRoutingTable(@NonNull android.app.Activity, @Nullable String, @Nullable String);
+    method @FlaggedApi("android.nfc.nfc_override_recover_routing_table") public void recoverRoutingTable(@NonNull android.app.Activity);
   }
 
 }
diff --git a/nfc/java/android/nfc/INfcCardEmulation.aidl b/nfc/java/android/nfc/INfcCardEmulation.aidl
index cb97f23..79f1275 100644
--- a/nfc/java/android/nfc/INfcCardEmulation.aidl
+++ b/nfc/java/android/nfc/INfcCardEmulation.aidl
@@ -48,6 +48,6 @@
     boolean setServiceEnabledForCategoryOther(int userHandle, in ComponentName app, boolean status);
     boolean isDefaultPaymentRegistered();
 
-    boolean overrideRoutingTable(int userHandle, String protocol, String technology);
-    boolean recoverRoutingTable(int userHandle);
+    void overrideRoutingTable(int userHandle, String protocol, String technology, in String pkg);
+    void recoverRoutingTable(int userHandle);
 }
diff --git a/nfc/java/android/nfc/cardemulation/CardEmulation.java b/nfc/java/android/nfc/cardemulation/CardEmulation.java
index e0438ce..03372b2 100644
--- a/nfc/java/android/nfc/cardemulation/CardEmulation.java
+++ b/nfc/java/android/nfc/cardemulation/CardEmulation.java
@@ -23,6 +23,7 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.StringDef;
 import android.annotation.SystemApi;
 import android.annotation.UserHandleAware;
 import android.annotation.UserIdInt;
@@ -43,6 +44,8 @@
 import android.provider.Settings.SettingNotFoundException;
 import android.util.Log;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.HashMap;
 import java.util.HexFormat;
 import java.util.List;
@@ -148,6 +151,21 @@
      *    that service will be invoked directly.
      */
     public static final int SELECTION_MODE_ASK_IF_CONFLICT = 2;
+    /**
+     * Route to Device Host (DH).
+     */
+    @FlaggedApi(Flags.FLAG_NFC_OVERRIDE_RECOVER_ROUTING_TABLE)
+    public static final String DH = "DH";
+    /**
+     * Route to eSE.
+     */
+    @FlaggedApi(Flags.FLAG_NFC_OVERRIDE_RECOVER_ROUTING_TABLE)
+    public static final String ESE = "ESE";
+    /**
+     * Route to UICC.
+     */
+    @FlaggedApi(Flags.FLAG_NFC_OVERRIDE_RECOVER_ROUTING_TABLE)
+    public static final String UICC = "UICC";
 
     static boolean sIsInitialized = false;
     static HashMap<Context, CardEmulation> sCardEmus = new HashMap<Context, CardEmulation>();
@@ -865,11 +883,22 @@
                 sService.setServiceEnabledForCategoryOther(userId, service, status), false);
     }
 
+    /** @hide */
+    @StringDef({
+        DH,
+        ESE,
+        UICC
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ProtocolAndTechnologyRoute {}
+
      /**
       * Setting NFC controller routing table, which includes Protocol Route and Technology Route,
       * while this Activity is in the foreground.
       *
-      * The parameter set to null can be used to keep current values for that entry.
+      * The parameter set to null can be used to keep current values for that entry. Either
+      * Protocol Route or Technology Route should be override when calling this API, otherwise
+      * throw {@link IllegalArgumentException}.
       * <p>
       * Example usage in an Activity that requires to set proto route to "ESE" and keep tech route:
       * <pre>
@@ -877,26 +906,39 @@
       *     mNfcAdapter.overrideRoutingTable(this , "ESE" , null);
       * }</pre>
       * </p>
-      * Also activities must call this method when it goes to the background,
-      * with all parameters set to null.
+      * Also activities must call {@link #recoverRoutingTable(Activity)}
+      * when it goes to the background. Only the package of the
+      * currently preferred service (the service set as preferred by the current foreground
+      * application via {@link CardEmulation#setPreferredService(Activity, ComponentName)} or the
+      * current Default Wallet Role Holder {@link android.app.role.RoleManager#ROLE_WALLET}),
+      * otherwise a call to this method will fail and throw {@link SecurityException}.
       * @param activity The Activity that requests NFC controller routing table to be changed.
       * @param protocol ISO-DEP route destination, which can be "DH" or "UICC" or "ESE".
       * @param technology Tech-A, Tech-B route destination, which can be "DH" or "UICC" or "ESE".
-      * @return true if operation is successful and false otherwise
-      *
+      * @throws SecurityException if the caller is not the preferred NFC service
+      * @throws IllegalArgumentException if the activity is not resumed or the caller is not in the
+      * foreground, or both protocol route and technology route are null.
+      * <p>
       * This is a high risk API and only included to support mainline effort
       * @hide
       */
-    public boolean overrideRoutingTable(Activity activity, String protocol, String technology) {
-        if (activity == null) {
-            throw new NullPointerException("activity or service or category is null");
-        }
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_NFC_OVERRIDE_RECOVER_ROUTING_TABLE)
+    public void overrideRoutingTable(
+            @NonNull Activity activity, @ProtocolAndTechnologyRoute @Nullable String protocol,
+            @ProtocolAndTechnologyRoute @Nullable String technology) {
         if (!activity.isResumed()) {
             throw new IllegalArgumentException("Activity must be resumed.");
         }
-        return callServiceReturn(() ->
+        if (protocol == null && technology == null) {
+            throw new IllegalArgumentException(("Both Protocol and Technology are null."));
+        }
+        callService(() ->
                 sService.overrideRoutingTable(
-                    mContext.getUser().getIdentifier(), protocol, technology), false);
+                    mContext.getUser().getIdentifier(),
+                    protocol,
+                    technology,
+                    mContext.getPackageName()));
     }
 
     /**
@@ -904,20 +946,19 @@
      * which was changed by {@link #overrideRoutingTable(Activity, String, String)}
      *
      * @param activity The Activity that requested NFC controller routing table to be changed.
-     * @return true if operation is successful and false otherwise
+     * @throws IllegalArgumentException if the caller is not in the foreground.
      *
      * @hide
      */
-    public boolean recoverRoutingTable(Activity activity) {
-        if (activity == null) {
-            throw new NullPointerException("activity is null");
-        }
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_NFC_OVERRIDE_RECOVER_ROUTING_TABLE)
+    public void recoverRoutingTable(@NonNull Activity activity) {
         if (!activity.isResumed()) {
             throw new IllegalArgumentException("Activity must be resumed.");
         }
-        return callServiceReturn(() ->
+        callService(() ->
                 sService.recoverRoutingTable(
-                    mContext.getUser().getIdentifier()), false);
+                    mContext.getUser().getIdentifier()));
     }
 
     /**
diff --git a/nfc/java/android/nfc/flags.aconfig b/nfc/java/android/nfc/flags.aconfig
index 5819b98..0fda91d 100644
--- a/nfc/java/android/nfc/flags.aconfig
+++ b/nfc/java/android/nfc/flags.aconfig
@@ -133,3 +133,11 @@
     description: "Add Settings.ACTION_MANAGE_OTHER_NFC_SERVICES_SETTINGS"
     bug: "358129872"
 }
+
+flag {
+    name: "nfc_override_recover_routing_table"
+    is_exported: true
+    namespace: "nfc"
+    description: "Enable override and recover routing table"
+    bug: "329043523"
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveErrorFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveErrorFragment.java
index d33433f..2fb32a7 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveErrorFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveErrorFragment.java
@@ -16,10 +16,12 @@
 
 package com.android.packageinstaller;
 
+import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 
 import android.app.Activity;
 import android.app.AlertDialog;
+import android.app.BroadcastOptions;
 import android.app.Dialog;
 import android.app.DialogFragment;
 import android.app.PendingIntent;
@@ -161,25 +163,31 @@
             return;
         }
 
+        // Allow the error handling actvities to start in the background.
+        final BroadcastOptions options = BroadcastOptions.makeBasic();
+        options.setPendingIntentBackgroundActivityStartMode(
+                MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
         switch (mStatus) {
             case PackageInstaller.UNARCHIVAL_ERROR_USER_ACTION_NEEDED:
                 activity.startIntentSender(mExtraIntent.getIntentSender(), /* fillInIntent= */
-                        null, /* flagsMask= */ 0, FLAG_ACTIVITY_NEW_TASK, /* extraFlags= */ 0);
+                        null, /* flagsMask= */ 0, FLAG_ACTIVITY_NEW_TASK, /* extraFlags= */ 0,
+                        options.toBundle());
                 break;
             case PackageInstaller.UNARCHIVAL_ERROR_INSUFFICIENT_STORAGE:
                 if (mExtraIntent != null) {
                     activity.startIntentSender(mExtraIntent.getIntentSender(), /* fillInIntent= */
-                            null, /* flagsMask= */ 0, FLAG_ACTIVITY_NEW_TASK, /* extraFlags= */ 0);
+                            null, /* flagsMask= */ 0, FLAG_ACTIVITY_NEW_TASK, /* extraFlags= */ 0,
+                            options.toBundle());
                 } else {
                     Intent intent = new Intent("android.intent.action.MANAGE_PACKAGE_STORAGE");
-                    startActivity(intent);
+                    startActivity(intent, options.toBundle());
                 }
                 break;
             case PackageInstaller.UNARCHIVAL_ERROR_INSTALLER_DISABLED:
                 Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                 Uri uri = Uri.fromParts("package", mInstallerPackageName, null);
                 intent.setData(uri);
-                startActivity(intent);
+                startActivity(intent, options.toBundle());
                 break;
             default:
                 // Do nothing. The rest of the dialogs are purely informational.
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index f0c8894..823ff9f 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -1053,4 +1053,15 @@
 
     <!-- List of packages for which we want to use activity info (instead of application info) for biometric prompt logo. Empty for AOSP. [DO NOT TRANSLATE] -->
     <string-array name="config_useActivityLogoForBiometricPrompt" translatable="false"/>
+
+    <!--
+    Whether to enable the desktop specific feature set.
+
+    Refrain from using this from code that needs to make decisions
+    regarding the size or density of the display.
+
+    Variant owners and OEMs should override this to true when they want to
+    enable the desktop specific features.
+    -->
+    <bool name="config_enableDesktopFeatureSet">false</bool>
 </resources>
diff --git a/services/core/java/com/android/server/notification/NotificationChannelExtractor.java b/services/core/java/com/android/server/notification/NotificationChannelExtractor.java
index 1938642..e2889fa 100644
--- a/services/core/java/com/android/server/notification/NotificationChannelExtractor.java
+++ b/services/core/java/com/android/server/notification/NotificationChannelExtractor.java
@@ -29,8 +29,8 @@
 import android.media.AudioAttributes;
 import android.os.Binder;
 import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.util.Slog;
+
 import com.android.internal.compat.IPlatformCompat;
 
 /**
@@ -79,6 +79,11 @@
         if (restrictAudioAttributesCall() || restrictAudioAttributesAlarm()
                 || restrictAudioAttributesMedia()) {
             AudioAttributes attributes = record.getChannel().getAudioAttributes();
+            if (attributes == null) {
+                if (DBG) Slog.d(TAG, "missing AudioAttributes");
+                return null;
+            }
+
             boolean updateAttributes =  false;
             if (restrictAudioAttributesCall()
                     && !record.getNotification().isStyle(Notification.CallStyle.class)
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 22b4d5d..5105fd3 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -1399,7 +1399,29 @@
                                 "Package " + pkgName + " is a persistent app. "
                                         + "Persistent apps are not updateable.");
                     }
+                    // When updating an sdk library, make sure that the versionMajor is
+                    // changed if the targetSdkVersion and minSdkVersion have changed
+                    if (parsedPackage.isSdkLibrary() && ps.getPkg() != null
+                            && ps.getPkg().isSdkLibrary()) {
+                        final int oldMinSdk = ps.getPkg().getMinSdkVersion();
+                        final int newMinSdk = parsedPackage.getMinSdkVersion();
+                        if (oldTargetSdk != newTargetSdk || oldMinSdk != newMinSdk) {
+                            final int oldVersionMajor = ps.getPkg().getSdkLibVersionMajor();
+                            final int newVersionMajor = parsedPackage.getSdkLibVersionMajor();
+                            if (oldVersionMajor == newVersionMajor) {
+                                throw new PrepareFailure(
+                                        PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE,
+                                        "Failure updating " + pkgName + " as it updates"
+                                                + " an sdk library <"
+                                                + parsedPackage.getSdkLibraryName() + ">"
+                                                + " without changing the versionMajor, but the"
+                                                + " targetSdkVersion or minSdkVersion has changed."
+                                );
+                            }
+                        }
+                    }
                 }
+
             }
 
             PackageSetting signatureCheckPs = ps;
diff --git a/services/core/java/com/android/server/vibrator/VibrationScaler.java b/services/core/java/com/android/server/vibrator/VibrationScaler.java
index a74c4e0..b3862cc 100644
--- a/services/core/java/com/android/server/vibrator/VibrationScaler.java
+++ b/services/core/java/com/android/server/vibrator/VibrationScaler.java
@@ -134,7 +134,8 @@
         return effect.resolve(mDefaultVibrationAmplitude)
                 .applyEffectStrength(newEffectStrength)
                 .scale(scaleFactor)
-                .scaleLinearly(adaptiveScale);
+                // Make sure this is the last one so it is applied on top of the settings scaling.
+                .applyAdaptiveScale(adaptiveScale);
     }
 
     /**
diff --git a/services/core/java/com/android/server/vibrator/VibratorController.java b/services/core/java/com/android/server/vibrator/VibratorController.java
index 8cc157c..4fc0b74 100644
--- a/services/core/java/com/android/server/vibrator/VibratorController.java
+++ b/services/core/java/com/android/server/vibrator/VibratorController.java
@@ -279,8 +279,8 @@
                 vendorEffect.getVendorData().writeToParcel(vendorData, /* flags= */ 0);
                 vendorData.setDataPosition(0);
                 long duration = mNativeWrapper.performVendorEffect(vendorData,
-                        vendorEffect.getEffectStrength(), vendorEffect.getLinearScale(),
-                        vibrationId);
+                        vendorEffect.getEffectStrength(), vendorEffect.getScale(),
+                        vendorEffect.getAdaptiveScale(), vibrationId);
                 if (duration > 0) {
                     mCurrentAmplitude = -1;
                     notifyListenerOnVibrating(true);
@@ -459,7 +459,7 @@
                 long vibrationId);
 
         private static native long performVendorEffect(long nativePtr, Parcel vendorData,
-                long strength, float scale, long vibrationId);
+                long strength, float scale, float adaptiveScale, long vibrationId);
 
         private static native long performComposedEffect(long nativePtr, PrimitiveSegment[] effect,
                 long vibrationId);
@@ -518,8 +518,9 @@
 
         /** Turns vibrator on to perform a vendor-specific effect. */
         public long performVendorEffect(Parcel vendorData, long strength, float scale,
-                long vibrationId) {
-            return performVendorEffect(mNativePtr, vendorData, strength, scale, vibrationId);
+                float adaptiveScale, long vibrationId) {
+            return performVendorEffect(mNativePtr, vendorData, strength, scale, adaptiveScale,
+                    vibrationId);
         }
 
         /** Turns vibrator on to perform effect composed of give primitives effect. */
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index d169b1e..3ac91b3 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -3292,12 +3292,6 @@
             return false;
         }
 
-        // Check if this activity is the top activity of its task - this prevents any trampolines
-        // followed by enterPictureInPictureMode() calls by an activity from below in its stack.
-        if (getTask().getTopMostActivity() != this) {
-            return false;
-        }
-
         // Check to see if PiP is supported for the display this container is on.
         if (mDisplayContent != null && !mDisplayContent.mDwpcHelper.isEnteringPipAllowed(
                 getUid())) {
diff --git a/services/core/jni/com_android_server_vibrator_VibratorController.cpp b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
index f12930a..5c5ac28 100644
--- a/services/core/jni/com_android_server_vibrator_VibratorController.cpp
+++ b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
@@ -198,7 +198,8 @@
 }
 
 static Aidl::VendorEffect vendorEffectFromJavaParcel(JNIEnv* env, jobject vendorData,
-                                                     jlong strength, jfloat scale) {
+                                                     jlong strength, jfloat scale,
+                                                     jfloat adaptiveScale) {
     PersistableBundle bundle;
     if (AParcel* parcel = AParcel_fromJavaParcel(env, vendorData); parcel != nullptr) {
         if (binder_status_t status = bundle.readFromParcel(parcel); status == STATUS_OK) {
@@ -217,6 +218,7 @@
     effect.vendorData = bundle;
     effect.strength = static_cast<Aidl::EffectStrength>(strength);
     effect.scale = static_cast<float>(scale);
+    effect.vendorScale = static_cast<float>(adaptiveScale);
     return effect;
 }
 
@@ -319,13 +321,14 @@
 
 static jlong vibratorPerformVendorEffect(JNIEnv* env, jclass /* clazz */, jlong ptr,
                                          jobject vendorData, jlong strength, jfloat scale,
-                                         jlong vibrationId) {
+                                         jfloat adaptiveScale, jlong vibrationId) {
     VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr);
     if (wrapper == nullptr) {
         ALOGE("vibratorPerformVendorEffect failed because native wrapper was not initialized");
         return -1;
     }
-    Aidl::VendorEffect effect = vendorEffectFromJavaParcel(env, vendorData, strength, scale);
+    Aidl::VendorEffect effect =
+            vendorEffectFromJavaParcel(env, vendorData, strength, scale, adaptiveScale);
     auto callback = wrapper->createCallback(vibrationId);
     auto performVendorEffectFn = [&effect, &callback](vibrator::HalWrapper* hal) {
         return hal->performVendorEffect(effect, callback);
@@ -511,7 +514,7 @@
         {"off", "(J)V", (void*)vibratorOff},
         {"setAmplitude", "(JF)V", (void*)vibratorSetAmplitude},
         {"performEffect", "(JJJJ)J", (void*)vibratorPerformEffect},
-        {"performVendorEffect", "(JLandroid/os/Parcel;JFJ)J", (void*)vibratorPerformVendorEffect},
+        {"performVendorEffect", "(JLandroid/os/Parcel;JFFJ)J", (void*)vibratorPerformVendorEffect},
         {"performComposedEffect", "(J[Landroid/os/vibrator/PrimitiveSegment;J)J",
          (void*)vibratorPerformComposedEffect},
         {"performPwleEffect", "(J[Landroid/os/vibrator/RampSegment;IJ)J",
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java
index 4704691..9681d74 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java
@@ -281,8 +281,8 @@
 
     @Test
     @EnableFlags(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
-    public void scale_withVendorEffect_setsEffectStrengthBasedOnSettings() {
-        setDefaultIntensity(USAGE_NOTIFICATION, VIBRATION_INTENSITY_LOW);
+    public void scale_withVendorEffect_setsEffectStrengthAndScaleBasedOnSettings() {
+        setDefaultIntensity(USAGE_NOTIFICATION, VIBRATION_INTENSITY_MEDIUM);
         setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_HIGH);
         PersistableBundle vendorData = new PersistableBundle();
         vendorData.putString("key", "value");
@@ -291,20 +291,27 @@
         VibrationEffect.VendorEffect scaled =
                 (VibrationEffect.VendorEffect) mVibrationScaler.scale(effect, USAGE_NOTIFICATION);
         assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_STRONG);
+        // Notification scales up.
+        assertTrue(scaled.getScale() > 1);
 
         setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
                 VIBRATION_INTENSITY_MEDIUM);
         scaled = (VibrationEffect.VendorEffect) mVibrationScaler.scale(effect, USAGE_NOTIFICATION);
         assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_MEDIUM);
+        // Notification does not scale.
+        assertEquals(1, scaled.getScale(), TOLERANCE);
 
         setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW);
         scaled = (VibrationEffect.VendorEffect) mVibrationScaler.scale(effect, USAGE_NOTIFICATION);
         assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_LIGHT);
+        // Notification scales down.
+        assertTrue(scaled.getScale() < 1);
 
         setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
         scaled = (VibrationEffect.VendorEffect) mVibrationScaler.scale(effect, USAGE_NOTIFICATION);
         // Vibration setting being bypassed will use default setting.
-        assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_LIGHT);
+        assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_MEDIUM);
+        assertEquals(1, scaled.getScale(), TOLERANCE);
     }
 
     @Test
@@ -348,7 +355,7 @@
         scaled = getFirstSegment(mVibrationScaler.scale(VibrationEffect.createOneShot(128, 128),
                 USAGE_TOUCH));
         // Haptic feedback does not scale.
-        assertEquals(128f / 255, scaled.getAmplitude(), 1e-5);
+        assertEquals(128f / 255, scaled.getAmplitude(), TOLERANCE);
     }
 
     @Test
@@ -373,7 +380,7 @@
 
         scaled = getFirstSegment(mVibrationScaler.scale(composed, USAGE_TOUCH));
         // Haptic feedback does not scale.
-        assertEquals(0.5, scaled.getScale(), 1e-5);
+        assertEquals(0.5, scaled.getScale(), TOLERANCE);
     }
 
     @Test
@@ -446,7 +453,7 @@
             android.os.vibrator.Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED,
             android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS,
     })
-    public void scale_adaptiveHapticsOnVendorEffect_setsLinearScaleParameter() {
+    public void scale_adaptiveHapticsOnVendorEffect_setsAdaptiveScaleParameter() {
         setDefaultIntensity(USAGE_RINGTONE, VIBRATION_INTENSITY_HIGH);
 
         mVibrationScaler.updateAdaptiveHapticsScale(USAGE_RINGTONE, 0.5f);
@@ -457,12 +464,12 @@
 
         VibrationEffect.VendorEffect scaled =
                 (VibrationEffect.VendorEffect) mVibrationScaler.scale(effect, USAGE_RINGTONE);
-        assertEquals(scaled.getLinearScale(), 0.5f);
+        assertEquals(scaled.getAdaptiveScale(), 0.5f);
 
         mVibrationScaler.removeAdaptiveHapticsScale(USAGE_RINGTONE);
 
         scaled = (VibrationEffect.VendorEffect) mVibrationScaler.scale(effect, USAGE_RINGTONE);
-        assertEquals(scaled.getLinearScale(), 1.0f);
+        assertEquals(scaled.getAdaptiveScale(), 1.0f);
     }
 
     private void setDefaultIntensity(@VibrationAttributes.Usage int usage,
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
index 0fbdce4..bfdaa78 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
@@ -34,13 +34,15 @@
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.ComponentName;
-import android.content.Context;
+import android.content.ContentResolver;
+import android.content.ContextWrapper;
 import android.content.pm.PackageManagerInternal;
 import android.hardware.vibrator.Braking;
 import android.hardware.vibrator.IVibrator;
@@ -52,6 +54,7 @@
 import android.os.PowerManager;
 import android.os.Process;
 import android.os.SystemClock;
+import android.os.UserHandle;
 import android.os.VibrationAttributes;
 import android.os.VibrationEffect;
 import android.os.Vibrator;
@@ -66,11 +69,14 @@
 import android.platform.test.annotations.RequiresFlagsEnabled;
 import android.platform.test.flag.junit.CheckFlagsRule;
 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.provider.Settings;
 import android.util.SparseArray;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.LargeTest;
 
+import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.internal.util.test.FakeSettingsProviderRule;
 import com.android.server.LocalServices;
 
 import org.junit.After;
@@ -105,10 +111,12 @@
     private static final int TEST_DEFAULT_AMPLITUDE = 255;
     private static final float TEST_DEFAULT_SCALE_LEVEL_GAIN = 1.4f;
 
-    @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
     @Rule
-    public final CheckFlagsRule mCheckFlagsRule =
-            DeviceFlagsValueProvider.createCheckFlagsRule();
+    public MockitoRule mMockitoRule = MockitoJUnit.rule();
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+    @Rule
+    public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
 
     @Mock private PackageManagerInternal mPackageManagerInternalMock;
     @Mock private VibrationThread.VibratorManagerHooks mManagerHooks;
@@ -117,6 +125,7 @@
     @Mock private VibrationConfig mVibrationConfigMock;
     @Mock private VibratorFrameworkStatsLogger mStatsLoggerMock;
 
+    private ContextWrapper mContextSpy;
     private final Map<Integer, FakeVibratorControllerProvider> mVibratorProviders = new HashMap<>();
     private VibrationSettings mVibrationSettings;
     private VibrationScaler mVibrationScaler;
@@ -149,14 +158,16 @@
         LocalServices.removeServiceForTest(PackageManagerInternal.class);
         LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternalMock);
 
-        Context context = InstrumentationRegistry.getContext();
-        mVibrationSettings = new VibrationSettings(context, new Handler(mTestLooper.getLooper()),
-                mVibrationConfigMock);
+        mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
+        ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy);
+        when(mContextSpy.getContentResolver()).thenReturn(contentResolver);
+        mVibrationSettings = new VibrationSettings(mContextSpy,
+                new Handler(mTestLooper.getLooper()), mVibrationConfigMock);
         mVibrationScaler = new VibrationScaler(mVibrationConfigMock, mVibrationSettings);
 
         mockVibrators(VIBRATOR_ID);
 
-        PowerManager.WakeLock wakeLock = context.getSystemService(
+        PowerManager.WakeLock wakeLock = mContextSpy.getSystemService(
                 PowerManager.class).newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*");
         mThread = new VibrationThread(wakeLock, mManagerHooks);
         mThread.start();
@@ -254,6 +265,9 @@
     @Test
     @RequiresFlagsEnabled(Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
     public void vibrate_singleWaveformWithAdaptiveHapticsScaling_scalesAmplitudesProperly() {
+        // No user settings scale.
+        setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
+                Vibrator.VIBRATION_INTENSITY_MEDIUM);
         mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
 
         VibrationEffect effect = VibrationEffect.createWaveform(
@@ -277,6 +291,9 @@
     @Test
     @RequiresFlagsEnabled(Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
     public void vibrate_withVibrationParamsRequestStalling_timeoutRequestAndApplyNoScaling() {
+        // No user settings scale.
+        setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
+                Vibrator.VIBRATION_INTENSITY_MEDIUM);
         mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
         VibrationEffect effect = VibrationEffect.createWaveform(
                 new long[]{5, 5, 5}, new int[]{1, 1, 1}, -1);
@@ -1864,6 +1881,13 @@
         }
     }
 
+    private void setUserSetting(String settingName, int value) {
+        Settings.System.putIntForUser(
+                mContextSpy.getContentResolver(), settingName, value, UserHandle.USER_CURRENT);
+        // FakeSettingsProvider doesn't support testing triggering ContentObserver yet.
+        mVibrationSettings.mSettingObserver.onChange(false);
+    }
+
     private long startThreadAndDispatcher(VibrationEffect effect) {
         return startThreadAndDispatcher(CombinedVibration.createParallel(effect));
     }
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index f009229..4013587 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -1622,7 +1622,12 @@
 
         vibrateAndWaitUntilFinished(service, vendorEffect, RINGTONE_ATTRS);
 
-        assertThat(fakeVibrator.getAllVendorEffects()).containsExactly(vendorEffect);
+        // Compare vendor data only, ignore scale applied by device settings in this test.
+        assertThat(fakeVibrator.getAllVendorEffects()).hasSize(1);
+        assertThat(fakeVibrator.getAllVendorEffects().get(0).getVendorData().keySet())
+                .containsExactly("key");
+        assertThat(fakeVibrator.getAllVendorEffects().get(0).getVendorData().getString("key"))
+                .isEqualTo("value");
     }
 
     @Test
@@ -1765,7 +1770,8 @@
         assertThat(fakeVibrator.getAllVendorEffects()).hasSize(1);
         VibrationEffect.VendorEffect scaled = fakeVibrator.getAllVendorEffects().get(0);
         assertThat(scaled.getEffectStrength()).isEqualTo(VibrationEffect.EFFECT_STRENGTH_LIGHT);
-        assertThat(scaled.getLinearScale()).isEqualTo(0.4f);
+        assertThat(scaled.getScale()).isAtMost(1); // Scale down or none if default is LOW
+        assertThat(scaled.getAdaptiveScale()).isEqualTo(0.4f);
     }
 
     @Test
diff --git a/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java b/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java
index 96c3e97..031d1c2 100644
--- a/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java
+++ b/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java
@@ -140,13 +140,13 @@
 
         @Override
         public long performVendorEffect(Parcel vendorData, long strength, float scale,
-                long vibrationId) {
+                float adaptiveScale, long vibrationId) {
             if ((mCapabilities & IVibrator.CAP_PERFORM_VENDOR_EFFECTS) == 0) {
                 return 0;
             }
             PersistableBundle bundle = PersistableBundle.CREATOR.createFromParcel(vendorData);
             recordVendorEffect(vibrationId,
-                    new VibrationEffect.VendorEffect(bundle, (int) strength, scale));
+                    new VibrationEffect.VendorEffect(bundle, (int) strength, scale, adaptiveScale));
             applyLatency(mOnLatency);
             scheduleListener(mVendorEffectDuration, vibrationId);
             // HAL has unknown duration for vendor effects.
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt
index 638d594..eb63e49 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt
@@ -28,6 +28,7 @@
 import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
 import com.android.server.wm.flicker.navBarLayerIsVisibleAtStartAndEnd
 import com.android.server.wm.flicker.statusBarLayerIsVisibleAtStartAndEnd
+import com.android.server.wm.flicker.taskBarLayerIsVisibleAtStartAndEnd
 import org.junit.Assume
 import org.junit.FixMethodOrder
 import org.junit.Ignore
@@ -114,28 +115,28 @@
 
     /**
      * In the legacy transitions, the nav bar is not marked as invisible. In the new transitions
-     * this is fixed and the nav bar shows as invisible
+     * this is fixed and the status bar shows as invisible
      */
     @Presubmit
     @Test
     fun statusBarLayerIsInvisibleInLandscapePhone() {
         Assume.assumeTrue(flicker.scenario.isLandscapeOrSeascapeAtStart)
         Assume.assumeTrue(flicker.scenario.isGesturalNavigation)
-        Assume.assumeFalse(usesTaskbar)
+        Assume.assumeFalse(flicker.scenario.isTablet)
         flicker.assertLayersStart { this.isVisible(ComponentNameMatcher.STATUS_BAR) }
         flicker.assertLayersEnd { this.isInvisible(ComponentNameMatcher.STATUS_BAR) }
     }
 
     /**
      * In the legacy transitions, the nav bar is not marked as invisible. In the new transitions
-     * this is fixed and the nav bar shows as invisible
+     * this is fixed and the status bar shows as invisible
      */
     @Presubmit
     @Test
     fun statusBarLayerIsInvisibleInLandscapeTablet() {
         Assume.assumeTrue(flicker.scenario.isLandscapeOrSeascapeAtStart)
         Assume.assumeTrue(flicker.scenario.isGesturalNavigation)
-        Assume.assumeTrue(usesTaskbar)
+        Assume.assumeTrue(flicker.scenario.isTablet)
         flicker.statusBarLayerIsVisibleAtStartAndEnd()
     }
 
@@ -149,6 +150,10 @@
     @Ignore("Visibility changes depending on orientation and navigation mode")
     override fun navBarLayerPositionAtStartAndEnd() {}
 
+    @Test
+    @Ignore("Visibility changes depending on orientation and navigation mode")
+    override fun taskBarLayerIsVisibleAtStartAndEnd() {}
+
     /** {@inheritDoc} */
     @Test
     @Ignore("Visibility changes depending on orientation and navigation mode")
@@ -161,7 +166,10 @@
 
     @Presubmit
     @Test
-    override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd()
+    fun taskBarLayerIsVisibleAtStartAndEndForTablets() {
+        Assume.assumeTrue(flicker.scenario.isTablet)
+        flicker.taskBarLayerIsVisibleAtStartAndEnd()
+    }
 
     @Presubmit
     @Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt
index 70d762e..851ce02 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt
@@ -137,8 +137,6 @@
     /**
      * Checks that the [ComponentNameMatcher.TASK_BAR] window is visible at the start and end of the
      * transition
-     *
-     * Note: Large screen only
      */
     @Presubmit
     @Test
@@ -149,8 +147,6 @@
 
     /**
      * Checks that the [ComponentNameMatcher.TASK_BAR] window is visible during the whole transition
-     *
-     * Note: Large screen only
      */
     @Presubmit
     @Test
diff --git a/tests/Internal/AndroidTest.xml b/tests/Internal/AndroidTest.xml
index 7b67e9e..2d6c650e 100644
--- a/tests/Internal/AndroidTest.xml
+++ b/tests/Internal/AndroidTest.xml
@@ -26,4 +26,12 @@
         <option name="package" value="com.android.internal.tests" />
         <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
     </test>
+
+    <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+        <option name="pull-pattern-keys" value="perfetto_file_path"/>
+        <option name="directory-keys"
+            value="/data/user/0/com.android.internal.tests/files"/>
+        <option name="collect-on-run-ended-only" value="true"/>
+        <option name="clean-up" value="true"/>
+    </metrics_collector>
 </configuration>
\ No newline at end of file
diff --git a/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java b/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java
index ecaab12..4b745b2 100644
--- a/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java
+++ b/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java
@@ -29,7 +29,6 @@
 import static org.mockito.Mockito.when;
 
 import static java.io.File.createTempFile;
-import static java.nio.file.Files.createTempDirectory;
 
 import android.content.Context;
 import android.os.SystemClock;
@@ -45,6 +44,7 @@
 import android.util.proto.ProtoInputStream;
 
 import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
 
 import com.android.internal.protolog.common.IProtoLogGroup;
 import com.android.internal.protolog.common.LogDataType;
@@ -77,7 +77,8 @@
 @Presubmit
 @RunWith(JUnit4.class)
 public class PerfettoProtoLogImplTest {
-    private final File mTracingDirectory = createTempDirectory("temp").toFile();
+    private final File mTracingDirectory = InstrumentationRegistry.getInstrumentation()
+            .getTargetContext().getFilesDir();
 
     private final ResultWriter mWriter = new ResultWriter()
             .forScenario(new ScenarioBuilder()
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 1c85e9f..a5aecc8 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -151,6 +151,7 @@
   }
 
   if (res->value != nullptr) {
+    res->value->SetFlagStatus(res->flag_status);
     // Attach the comment, source and config to the value.
     res->value->SetComment(std::move(res->comment));
     res->value->SetSource(std::move(res->source));
@@ -546,30 +547,11 @@
   });
 
   std::string resource_type = parser->element_name();
-  std::optional<StringPiece> flag =
-      xml::FindAttribute(parser, "http://schemas.android.com/apk/res/android", "featureFlag");
-  out_resource->flag_status = FlagStatus::NoFlag;
-  if (flag) {
-    auto flag_it = options_.feature_flag_values.find(flag.value());
-    if (flag_it == options_.feature_flag_values.end()) {
-      diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number()))
-                   << "Resource flag value undefined");
-      return false;
-    }
-    const auto& flag_properties = flag_it->second;
-    if (!flag_properties.read_only) {
-      diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number()))
-                   << "Only read only flags may be used with resources");
-      return false;
-    }
-    if (!flag_properties.enabled.has_value()) {
-      diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number()))
-                   << "Only flags with a value may be used with resources");
-      return false;
-    }
-    out_resource->flag_status =
-        flag_properties.enabled.value() ? FlagStatus::Enabled : FlagStatus::Disabled;
+  auto flag_status = GetFlagStatus(parser);
+  if (!flag_status) {
+    return false;
   }
+  out_resource->flag_status = flag_status.value();
 
   // The value format accepted for this resource.
   uint32_t resource_format = 0u;
@@ -751,6 +733,33 @@
   return false;
 }
 
+std::optional<FlagStatus> ResourceParser::GetFlagStatus(xml::XmlPullParser* parser) {
+  auto flag_status = FlagStatus::NoFlag;
+
+  std::optional<StringPiece> flag = xml::FindAttribute(parser, xml::kSchemaAndroid, "featureFlag");
+  if (flag) {
+    auto flag_it = options_.feature_flag_values.find(flag.value());
+    if (flag_it == options_.feature_flag_values.end()) {
+      diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number()))
+                   << "Resource flag value undefined");
+      return {};
+    }
+    const auto& flag_properties = flag_it->second;
+    if (!flag_properties.read_only) {
+      diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number()))
+                   << "Only read only flags may be used with resources");
+      return {};
+    }
+    if (!flag_properties.enabled.has_value()) {
+      diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number()))
+                   << "Only flags with a value may be used with resources");
+      return {};
+    }
+    flag_status = flag_properties.enabled.value() ? FlagStatus::Enabled : FlagStatus::Disabled;
+  }
+  return flag_status;
+}
+
 bool ResourceParser::ParseItem(xml::XmlPullParser* parser,
                                ParsedResource* out_resource,
                                const uint32_t format) {
@@ -1657,12 +1666,18 @@
     const std::string& element_namespace = parser->element_namespace();
     const std::string& element_name = parser->element_name();
     if (element_namespace.empty() && element_name == "item") {
+      auto flag_status = GetFlagStatus(parser);
+      if (!flag_status) {
+        error = true;
+        continue;
+      }
       std::unique_ptr<Item> item = ParseXml(parser, typeMask, kNoRawString);
       if (!item) {
         diag_->Error(android::DiagMessage(item_source) << "could not parse array item");
         error = true;
         continue;
       }
+      item->SetFlagStatus(flag_status.value());
       item->SetSource(item_source);
       array->elements.emplace_back(std::move(item));
 
diff --git a/tools/aapt2/ResourceParser.h b/tools/aapt2/ResourceParser.h
index 45d41c1..442dea8 100644
--- a/tools/aapt2/ResourceParser.h
+++ b/tools/aapt2/ResourceParser.h
@@ -85,6 +85,8 @@
  private:
   DISALLOW_COPY_AND_ASSIGN(ResourceParser);
 
+  std::optional<FlagStatus> GetFlagStatus(xml::XmlPullParser* parser);
+
   std::optional<FlattenedXmlSubTree> CreateFlattenSubTree(xml::XmlPullParser* parser);
 
   // Parses the XML subtree as a StyleString (flattened XML representation for strings with
diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp
index 1cdb715..7a4f40e 100644
--- a/tools/aapt2/ResourceTable.cpp
+++ b/tools/aapt2/ResourceTable.cpp
@@ -605,12 +605,12 @@
     if (!config_value->value) {
       // Resource does not exist, add it now.
       config_value->value = std::move(res.value);
-      config_value->flag_status = res.flag_status;
     } else {
       // When validation is enabled, ensure that a resource cannot have multiple values defined for
       // the same configuration unless protected by flags.
-      auto result = validate ? ResolveFlagCollision(config_value->flag_status, res.flag_status)
-                             : CollisionResult::kKeepBoth;
+      auto result =
+          validate ? ResolveFlagCollision(config_value->value->GetFlagStatus(), res.flag_status)
+                   : CollisionResult::kKeepBoth;
       if (result == CollisionResult::kConflict) {
         result = ResolveValueCollision(config_value->value.get(), res.value.get());
       }
@@ -619,7 +619,6 @@
           // Insert the value ignoring for duplicate configurations
           entry->values.push_back(util::make_unique<ResourceConfigValue>(res.config, res.product));
           entry->values.back()->value = std::move(res.value);
-          entry->values.back()->flag_status = res.flag_status;
           break;
 
         case CollisionResult::kTakeNew:
diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h
index 4f76e7d..cba6b70 100644
--- a/tools/aapt2/ResourceTable.h
+++ b/tools/aapt2/ResourceTable.h
@@ -104,8 +104,6 @@
   // The actual Value.
   std::unique_ptr<Value> value;
 
-  FlagStatus flag_status = FlagStatus::NoFlag;
-
   ResourceConfigValue(const android::ConfigDescription& config, android::StringPiece product)
       : config(config), product(product) {
   }
diff --git a/tools/aapt2/ResourceValues.cpp b/tools/aapt2/ResourceValues.cpp
index 166b01b..b75e87c 100644
--- a/tools/aapt2/ResourceValues.cpp
+++ b/tools/aapt2/ResourceValues.cpp
@@ -971,6 +971,16 @@
   *out << "(array) [" << util::Joiner(elements, ", ") << "]";
 }
 
+void Array::RemoveFlagDisabledElements() {
+  const auto end_iter = elements.end();
+  const auto remove_iter = std::stable_partition(
+      elements.begin(), end_iter, [](const std::unique_ptr<Item>& item) -> bool {
+        return item->GetFlagStatus() != FlagStatus::Disabled;
+      });
+
+  elements.erase(remove_iter, end_iter);
+}
+
 bool Plural::Equals(const Value* value) const {
   const Plural* other = ValueCast<Plural>(value);
   if (!other) {
@@ -1092,6 +1102,7 @@
 std::unique_ptr<T> CopyValueFields(std::unique_ptr<T> new_value, const T* value) {
   new_value->SetSource(value->GetSource());
   new_value->SetComment(value->GetComment());
+  new_value->SetFlagStatus(value->GetFlagStatus());
   return new_value;
 }
 
diff --git a/tools/aapt2/ResourceValues.h b/tools/aapt2/ResourceValues.h
index 5192c2b..a1b1839 100644
--- a/tools/aapt2/ResourceValues.h
+++ b/tools/aapt2/ResourceValues.h
@@ -65,6 +65,14 @@
     return translatable_;
   }
 
+  void SetFlagStatus(FlagStatus val) {
+    flag_status_ = val;
+  }
+
+  FlagStatus GetFlagStatus() const {
+    return flag_status_;
+  }
+
   // Returns the source where this value was defined.
   const android::Source& GetSource() const {
     return source_;
@@ -109,6 +117,10 @@
   // of brevity and readability. Default implementation just calls Print().
   virtual void PrettyPrint(text::Printer* printer) const;
 
+  // Removes any part of the value that is beind a disabled flag.
+  virtual void RemoveFlagDisabledElements() {
+  }
+
   friend std::ostream& operator<<(std::ostream& out, const Value& value);
 
  protected:
@@ -116,6 +128,7 @@
   std::string comment_;
   bool weak_ = false;
   bool translatable_ = true;
+  FlagStatus flag_status_ = FlagStatus::NoFlag;
 
  private:
   virtual Value* TransformValueImpl(ValueTransformer& transformer) const = 0;
@@ -346,6 +359,7 @@
 
   bool Equals(const Value* value) const override;
   void Print(std::ostream* out) const override;
+  void RemoveFlagDisabledElements() override;
 };
 
 struct Plural : public TransformableValue<Plural, BaseValue<Plural>> {
diff --git a/tools/aapt2/Resources.proto b/tools/aapt2/Resources.proto
index 2ecc82a..5c64089 100644
--- a/tools/aapt2/Resources.proto
+++ b/tools/aapt2/Resources.proto
@@ -246,7 +246,7 @@
 message ConfigValue {
   Configuration config = 1;
   Value value = 2;
-  uint32 flag_status = 3;
+  reserved 3;
 }
 
 // The generic meta-data for every value in a resource table.
@@ -280,6 +280,9 @@
     Id id = 6;
     Primitive prim = 7;
   }
+
+  // The status of the flag the value is behind if any
+  uint32 flag_status = 8;
 }
 
 // A CompoundValue is an abstract type. It represents a value that is a made of other values.
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 56f5288..be63f82 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -1878,7 +1878,7 @@
       for (auto& type : package->types) {
         for (auto& entry : type->entries) {
           for (auto& config_value : entry->values) {
-            if (config_value->flag_status == FlagStatus::Disabled) {
+            if (config_value->value->GetFlagStatus() == FlagStatus::Disabled) {
               config_value->value->Accept(&visitor);
             }
           }
diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp
index aaab315..55f5e56 100644
--- a/tools/aapt2/format/proto/ProtoDeserialize.cpp
+++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp
@@ -534,8 +534,6 @@
           return false;
         }
 
-        config_value->flag_status = (FlagStatus)pb_config_value.flag_status();
-
         config_value->value = DeserializeValueFromPb(pb_config_value.value(), src_pool, config,
                                                      &out_table->string_pool, files, out_error);
         if (config_value->value == nullptr) {
@@ -877,11 +875,12 @@
   return value;
 }
 
-std::unique_ptr<Item> DeserializeItemFromPb(const pb::Item& pb_item,
-                                            const android::ResStringPool& src_pool,
-                                            const ConfigDescription& config,
-                                            android::StringPool* value_pool,
-                                            io::IFileCollection* files, std::string* out_error) {
+std::unique_ptr<Item> DeserializeItemFromPbInternal(const pb::Item& pb_item,
+                                                    const android::ResStringPool& src_pool,
+                                                    const ConfigDescription& config,
+                                                    android::StringPool* value_pool,
+                                                    io::IFileCollection* files,
+                                                    std::string* out_error) {
   switch (pb_item.value_case()) {
     case pb::Item::kRef: {
       const pb::Reference& pb_ref = pb_item.ref();
@@ -1010,6 +1009,19 @@
   return {};
 }
 
+std::unique_ptr<Item> DeserializeItemFromPb(const pb::Item& pb_item,
+                                            const android::ResStringPool& src_pool,
+                                            const ConfigDescription& config,
+                                            android::StringPool* value_pool,
+                                            io::IFileCollection* files, std::string* out_error) {
+  auto item =
+      DeserializeItemFromPbInternal(pb_item, src_pool, config, value_pool, files, out_error);
+  if (item) {
+    item->SetFlagStatus((FlagStatus)pb_item.flag_status());
+  }
+  return item;
+}
+
 std::unique_ptr<xml::XmlResource> DeserializeXmlResourceFromPb(const pb::XmlNode& pb_node,
                                                                std::string* out_error) {
   if (!pb_node.has_element()) {
diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp
index c1e15bc..5772b3b 100644
--- a/tools/aapt2/format/proto/ProtoSerialize.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize.cpp
@@ -426,7 +426,6 @@
           pb_config_value->mutable_config()->set_product(config_value->product);
           SerializeValueToPb(*config_value->value, pb_config_value->mutable_value(),
                              source_pool.get());
-          pb_config_value->set_flag_status((uint32_t)config_value->flag_status);
         }
       }
     }
@@ -720,6 +719,9 @@
   if (src_pool != nullptr) {
     SerializeSourceToPb(value.GetSource(), src_pool, out_value->mutable_source());
   }
+  if (out_value->has_item()) {
+    out_value->mutable_item()->set_flag_status((uint32_t)value.GetFlagStatus());
+  }
 }
 
 void SerializeItemToPb(const Item& item, pb::Item* out_item) {
@@ -727,6 +729,7 @@
   ValueSerializer serializer(&value, nullptr);
   item.Accept(&serializer);
   out_item->MergeFrom(value.item());
+  out_item->set_flag_status((uint32_t)item.GetFlagStatus());
 }
 
 void SerializeCompiledFileToPb(const ResourceFile& file, pb::internal::CompiledFile* out_file) {
diff --git a/tools/aapt2/integration-tests/FlaggedResourcesTest/Android.bp b/tools/aapt2/integration-tests/FlaggedResourcesTest/Android.bp
index 5932271..4866d2c 100644
--- a/tools/aapt2/integration-tests/FlaggedResourcesTest/Android.bp
+++ b/tools/aapt2/integration-tests/FlaggedResourcesTest/Android.bp
@@ -28,11 +28,13 @@
     srcs: [
         "res/values/bools.xml",
         "res/values/bools2.xml",
+        "res/values/ints.xml",
         "res/values/strings.xml",
     ],
     out: [
         "values_bools.arsc.flat",
         "values_bools2.arsc.flat",
+        "values_ints.arsc.flat",
         "values_strings.arsc.flat",
     ],
     cmd: "$(location aapt2) compile $(in) -o $(genDir) " +
diff --git a/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/ints.xml b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/ints.xml
new file mode 100644
index 0000000..26a5c40
--- /dev/null
+++ b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/ints.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+    <integer-array name="intarr1">
+        <item>1</item>
+        <item>2</item>
+        <item android:featureFlag="test.package.falseFlag">666</item>
+        <item>3</item>
+    </integer-array>
+</resources>
\ No newline at end of file
diff --git a/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/strings.xml b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/strings.xml
index 5c0fca1..3cbb928 100644
--- a/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/strings.xml
+++ b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/strings.xml
@@ -3,4 +3,11 @@
     <string name="str">plain string</string>
 
     <string name="str1" android:featureFlag="test.package.falseFlag">DONTFIND</string>
+
+    <string-array name="strarr1">
+        <item>one</item>
+        <item>two</item>
+        <item android:featureFlag="test.package.falseFlag">remove</item>
+        <item android:featureFlag="test.package.trueFlag">three</item>
+    </string-array>
 </resources>
\ No newline at end of file
diff --git a/tools/aapt2/link/FlagDisabledResourceRemover.cpp b/tools/aapt2/link/FlagDisabledResourceRemover.cpp
index e3289e2..3ac1762 100644
--- a/tools/aapt2/link/FlagDisabledResourceRemover.cpp
+++ b/tools/aapt2/link/FlagDisabledResourceRemover.cpp
@@ -32,12 +32,17 @@
   const auto remove_iter =
       std::stable_partition(entry->values.begin(), end_iter,
                             [](const std::unique_ptr<ResourceConfigValue>& value) -> bool {
-                              return value->flag_status != FlagStatus::Disabled;
+                              return value->value->GetFlagStatus() != FlagStatus::Disabled;
                             });
 
   bool keep = remove_iter != entry->values.begin();
 
   entry->values.erase(remove_iter, end_iter);
+
+  for (auto& value : entry->values) {
+    value->value->RemoveFlagDisabledElements();
+  }
+
   return keep;
 }
 
diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp
index 1942fc11..37a039e 100644
--- a/tools/aapt2/link/TableMerger.cpp
+++ b/tools/aapt2/link/TableMerger.cpp
@@ -212,8 +212,8 @@
     collision_result =
         ResolveMergeCollision(override_styles_instead_of_overlaying, dst_value, src_value, pool);
   } else {
-    collision_result = ResourceTable::ResolveFlagCollision(dst_config_value->flag_status,
-                                                           src_config_value->flag_status);
+    collision_result =
+        ResourceTable::ResolveFlagCollision(dst_value->GetFlagStatus(), src_value->GetFlagStatus());
     if (collision_result == CollisionResult::kConflict) {
       collision_result = ResourceTable::ResolveValueCollision(dst_value, src_value);
     }
@@ -295,7 +295,6 @@
         } else {
           dst_config_value =
               dst_entry->FindOrCreateValue(src_config_value->config, src_config_value->product);
-          dst_config_value->flag_status = src_config_value->flag_status;
         }
 
         // Continue if we're taking the new resource.