Merge "Merge SV transactions to VRI BlastBufferQueue"
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index ce16a78..36348b3 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -122,17 +122,26 @@
      * @hide
      */
     public static final int RULE_REJECT_ALL = 1 << 6;
+    /**
+     * Reject traffic on all networks for restricted networking mode.
+     */
+    public static final int RULE_REJECT_RESTRICTED_MODE = 1 << 10;
 
     /**
      * Mask used to get the {@code RULE_xxx_METERED} rules
      * @hide
      */
-    public static final int MASK_METERED_NETWORKS = 0b00001111;
+    public static final int MASK_METERED_NETWORKS = 0b000000001111;
     /**
      * Mask used to get the {@code RULE_xxx_ALL} rules
      * @hide
      */
-    public static final int MASK_ALL_NETWORKS     = 0b11110000;
+    public static final int MASK_ALL_NETWORKS     = 0b000011110000;
+    /**
+     * Mask used to get the {@code RULE_xxx_RESTRICTED_MODE} rules
+     * @hide
+     */
+    public static final int MASK_RESTRICTED_MODE_NETWORKS     = 0b111100000000;
 
     /** @hide */
     public static final int FIREWALL_RULE_DEFAULT = 0;
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 342c4b3..4632621 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -14627,6 +14627,17 @@
          */
         public static final String MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH =
                 "maximum_obscuring_opacity_for_touch";
+
+        /**
+         * Used to enable / disable the Restricted Networking Mode in which network access is
+         * restricted to apps holding the CONNECTIVITY_USE_RESTRICTED_NETWORKS permission.
+         *
+         * Values are:
+         * 0: disabled
+         * 1: enabled
+         * @hide
+         */
+        public static final String RESTRICTED_NETWORKING_MODE = "restricted_networking_mode";
     }
 
     /**
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 80437be..49cd3a6 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -1334,6 +1334,23 @@
 
     @VisibleForTesting
     public void applyAnimation(@InsetsType final int types, boolean show, boolean fromIme) {
+        // TODO(b/166736352): We should only skip the animation of specific types, not all types.
+        boolean skipAnim = false;
+        if ((types & ime()) != 0) {
+            final InsetsSourceConsumer consumer = mSourceConsumers.get(ITYPE_IME);
+            final InsetsSourceControl imeControl = consumer != null ? consumer.getControl() : null;
+            // Skip showing animation once that made by system for some reason.
+            // (e.g. starting window with IME snapshot)
+            if (imeControl != null && show) {
+                skipAnim = imeControl.getAndClearSkipAnimationOnce();
+            }
+        }
+        applyAnimation(types, show, fromIme, skipAnim);
+    }
+
+    @VisibleForTesting
+    public void applyAnimation(@InsetsType final int types, boolean show, boolean fromIme,
+            boolean skipAnim) {
         if (types == 0) {
             // nothing to animate.
             if (DEBUG) Log.d(TAG, "applyAnimation, nothing to animate");
@@ -1342,7 +1359,7 @@
 
         boolean hasAnimationCallbacks = mHost.hasAnimationCallbacks();
         final InternalAnimationControlListener listener = new InternalAnimationControlListener(
-                show, hasAnimationCallbacks, types, mAnimationsDisabled,
+                show, hasAnimationCallbacks, types, skipAnim || mAnimationsDisabled,
                 mHost.dipToPx(InternalAnimationControlListener.FLOATING_IME_BOTTOM_INSET));
 
         // Show/hide animations always need to be relative to the display frame, in order that shown
diff --git a/core/java/android/view/InsetsSourceControl.java b/core/java/android/view/InsetsSourceControl.java
index 5ddbd02..c717c2a 100644
--- a/core/java/android/view/InsetsSourceControl.java
+++ b/core/java/android/view/InsetsSourceControl.java
@@ -41,6 +41,7 @@
     private final @InternalInsetsType int mType;
     private final @Nullable SurfaceControl mLeash;
     private final Point mSurfacePosition;
+    private boolean mSkipAnimationOnce;
 
     public InsetsSourceControl(@InternalInsetsType int type, @Nullable SurfaceControl leash,
             Point surfacePosition) {
@@ -57,6 +58,7 @@
             mLeash = null;
         }
         mSurfacePosition = new Point(other.mSurfacePosition);
+        mSkipAnimationOnce = other.getAndClearSkipAnimationOnce();
     }
 
     public int getType() {
@@ -77,6 +79,7 @@
         mType = in.readInt();
         mLeash = in.readParcelable(null /* loader */);
         mSurfacePosition = in.readParcelable(null /* loader */);
+        mSkipAnimationOnce = in.readBoolean();
     }
 
     public boolean setSurfacePosition(int left, int top) {
@@ -87,10 +90,27 @@
         return true;
     }
 
+    public void setSkipAnimationOnce(boolean skipAnimation) {
+        mSkipAnimationOnce = skipAnimation;
+    }
+
     public Point getSurfacePosition() {
         return mSurfacePosition;
     }
 
+    /**
+     * Get the state whether the current control needs to skip animation or not.
+     *
+     * Note that this is a one-time check that the state is only valid and can be called when
+     * {@link InsetsController#applyAnimation} to check if the current control can skip animation
+     * at this time, and then will clear the state value.
+     */
+    public boolean getAndClearSkipAnimationOnce() {
+        final boolean result = mSkipAnimationOnce;
+        mSkipAnimationOnce = false;
+        return result;
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -101,6 +121,7 @@
         dest.writeInt(mType);
         dest.writeParcelable(mLeash, 0 /* flags*/);
         dest.writeParcelable(mSurfacePosition, 0 /* flags*/);
+        dest.writeBoolean(mSkipAnimationOnce);
     }
 
     public void release(Consumer<SurfaceControl> surfaceReleaseConsumer) {
@@ -114,6 +135,7 @@
         pw.print("InsetsSourceControl type="); pw.print(InsetsState.typeToString(mType));
         pw.print(" mLeash="); pw.print(mLeash);
         pw.print(" mSurfacePosition="); pw.print(mSurfacePosition);
+        pw.print(" mSkipAnimationOnce="); pw.print(mSkipAnimationOnce);
         pw.println();
     }
 
diff --git a/core/java/android/window/TaskSnapshot.java b/core/java/android/window/TaskSnapshot.java
index 9834aad..dc07e44 100644
--- a/core/java/android/window/TaskSnapshot.java
+++ b/core/java/android/window/TaskSnapshot.java
@@ -60,6 +60,7 @@
     private final @WindowInsetsController.Appearance
     int mAppearance;
     private final boolean mIsTranslucent;
+    private final boolean mHasImeSurface;
     // Must be one of the named color spaces, otherwise, always use SRGB color space.
     private final ColorSpace mColorSpace;
 
@@ -68,7 +69,7 @@
             @NonNull ColorSpace colorSpace, int orientation, int rotation, Point taskSize,
             Rect contentInsets, boolean isLowResolution, boolean isRealSnapshot,
             int windowingMode, @WindowInsetsController.Appearance int appearance,
-            boolean isTranslucent) {
+            boolean isTranslucent, boolean hasImeSurface) {
         mId = id;
         mTopActivityComponent = topActivityComponent;
         mSnapshot = snapshot;
@@ -83,6 +84,7 @@
         mWindowingMode = windowingMode;
         mAppearance = appearance;
         mIsTranslucent = isTranslucent;
+        mHasImeSurface = hasImeSurface;
     }
 
     private TaskSnapshot(Parcel source) {
@@ -102,6 +104,7 @@
         mWindowingMode = source.readInt();
         mAppearance = source.readInt();
         mIsTranslucent = source.readBoolean();
+        mHasImeSurface = source.readBoolean();
     }
 
     /**
@@ -201,6 +204,13 @@
     }
 
     /**
+     * @return Whether or not the snapshot has the IME surface.
+     */
+    public boolean hasImeSurface() {
+        return mHasImeSurface;
+    }
+
+    /**
      * @return The windowing mode of the task when this snapshot was taken.
      */
     public int getWindowingMode() {
@@ -237,6 +247,7 @@
         dest.writeInt(mWindowingMode);
         dest.writeInt(mAppearance);
         dest.writeBoolean(mIsTranslucent);
+        dest.writeBoolean(mHasImeSurface);
     }
 
     @Override
@@ -256,7 +267,8 @@
                 + " mIsRealSnapshot=" + mIsRealSnapshot
                 + " mWindowingMode=" + mWindowingMode
                 + " mAppearance=" + mAppearance
-                + " mIsTranslucent=" + mIsTranslucent;
+                + " mIsTranslucent=" + mIsTranslucent
+                + " mHasImeSurface=" + mHasImeSurface;
     }
 
     public static final @NonNull Creator<TaskSnapshot> CREATOR = new Creator<TaskSnapshot>() {
@@ -283,6 +295,7 @@
         private @WindowInsetsController.Appearance
         int mAppearance;
         private boolean mIsTranslucent;
+        private boolean mHasImeSurface;
         private int mPixelFormat;
 
         public Builder setId(long id) {
@@ -290,8 +303,7 @@
             return this;
         }
 
-        public Builder setTopActivityComponent(
-                ComponentName name) {
+        public Builder setTopActivityComponent(ComponentName name) {
             mTopActivity = name;
             return this;
         }
@@ -329,8 +341,7 @@
             return this;
         }
 
-        public Builder setIsRealSnapshot(
-                boolean realSnapshot) {
+        public Builder setIsRealSnapshot(boolean realSnapshot) {
             mIsRealSnapshot = realSnapshot;
             return this;
         }
@@ -340,18 +351,24 @@
             return this;
         }
 
-        public Builder setAppearance(
-                @WindowInsetsController.Appearance int appearance) {
+        public Builder setAppearance(@WindowInsetsController.Appearance int appearance) {
             mAppearance = appearance;
             return this;
         }
 
-        public Builder setIsTranslucent(
-                boolean isTranslucent) {
+        public Builder setIsTranslucent(boolean isTranslucent) {
             mIsTranslucent = isTranslucent;
             return this;
         }
 
+        /**
+         * Sets the IME visibility when taking the snapshot of the task.
+         */
+        public Builder setHasImeSurface(boolean hasImeSurface) {
+            mHasImeSurface = hasImeSurface;
+            return this;
+        }
+
         public int getPixelFormat() {
             return mPixelFormat;
         }
@@ -378,7 +395,8 @@
                     mIsRealSnapshot,
                     mWindowingMode,
                     mAppearance,
-                    mIsTranslucent);
+                    mIsTranslucent,
+                    mHasImeSurface);
 
         }
     }
diff --git a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
index db838e8..970ab79 100644
--- a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
@@ -128,4 +128,26 @@
                     eq(WindowInsets.Type.ime()), eq(false) /* show */, eq(true) /* fromIme */);
         });
     }
+
+    @Test
+    public void testImeGetAndClearSkipAnimationOnce() {
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            // Request IME visible before control is available.
+            mImeConsumer.onWindowFocusGained();
+            mImeConsumer.applyImeVisibility(true /* setVisible */);
+
+            // set control and verify visibility is applied.
+            InsetsSourceControl control = Mockito.spy(
+                    new InsetsSourceControl(ITYPE_IME, mLeash, new Point()));
+            // Simulate IME source control set this flag when the target has starting window.
+            control.setSkipAnimationOnce(true);
+            mController.onControlsChanged(new InsetsSourceControl[] { control });
+            // Verify IME show animation should be triggered when control becomes available and
+            // the animation will be skipped by getAndClearSkipAnimationOnce invoked.
+            verify(control).getAndClearSkipAnimationOnce();
+            verify(mController).applyAnimation(
+                    eq(WindowInsets.Type.ime()), eq(true) /* show */, eq(false) /* fromIme */,
+                    eq(true) /* skipAnim */);
+        });
+    }
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java
index 94af329..414a0a7 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java
@@ -96,7 +96,7 @@
                 ColorSpace.get(ColorSpace.Named.SRGB), ORIENTATION_PORTRAIT,
                 Surface.ROTATION_0, taskSize, contentInsets, false,
                 true /* isRealSnapshot */, WINDOWING_MODE_FULLSCREEN,
-                0 /* systemUiVisibility */, false /* isTranslucent */);
+                0 /* systemUiVisibility */, false /* isTranslucent */, false /* hasImeSurface */);
     }
 
     private static TaskDescription createTaskDescription(int background, int statusBar,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index 668e267..66165b6 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -146,5 +146,6 @@
         VALIDATORS.put(Global.CUSTOM_BUGREPORT_HANDLER_USER, ANY_INTEGER_VALIDATOR);
         VALIDATORS.put(Global.DEVELOPMENT_SETTINGS_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Global.NOTIFICATION_FEEDBACK_ENABLED, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Global.RESTRICTED_NETWORKING_MODE, BOOLEAN_VALIDATOR);
     }
 }
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 209218c..2070773 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -420,6 +420,7 @@
                     Settings.Global.RADIO_WIMAX,
                     Settings.Global.RECOMMENDED_NETWORK_EVALUATOR_CACHE_EXPIRY_MS,
                     Settings.Global.READ_EXTERNAL_STORAGE_ENFORCED_DEFAULT,
+                    Settings.Global.RESTRICTED_NETWORKING_MODE,
                     Settings.Global.REQUIRE_PASSWORD_TO_DECRYPT,
                     Settings.Global.SAFE_BOOT_DISALLOWED,
                     Settings.Global.SELINUX_STATUS,
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
index 901a736..77e568c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -37,16 +37,19 @@
 import com.android.keyguard.dagger.KeyguardStatusViewComponent;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.UiBackground;
 import com.android.systemui.navigationbar.NavigationBarController;
 import com.android.systemui.navigationbar.NavigationBarView;
 
+import java.util.concurrent.Executor;
+
 import javax.inject.Inject;
 
 public class KeyguardDisplayManager {
     protected static final String TAG = "KeyguardDisplayManager";
     private static boolean DEBUG = KeyguardConstants.DEBUG;
 
-    private final MediaRouter mMediaRouter;
+    private MediaRouter mMediaRouter = null;
     private final DisplayManager mDisplayService;
     private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
     private final Context mContext;
@@ -90,10 +93,11 @@
 
     @Inject
     public KeyguardDisplayManager(Context context,
-            KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory) {
+            KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory,
+            @UiBackground Executor uiBgExecutor) {
         mContext = context;
         mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory;
-        mMediaRouter = mContext.getSystemService(MediaRouter.class);
+        uiBgExecutor.execute(() -> mMediaRouter = mContext.getSystemService(MediaRouter.class));
         mDisplayService = mContext.getSystemService(DisplayManager.class);
         mDisplayService.registerDisplayListener(mDisplayListener, null /* handler */);
     }
@@ -162,8 +166,12 @@
     public void show() {
         if (!mShowing) {
             if (DEBUG) Log.v(TAG, "show");
-            mMediaRouter.addCallback(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY,
-                    mMediaRouterCallback, MediaRouter.CALLBACK_FLAG_PASSIVE_DISCOVERY);
+            if (mMediaRouter != null) {
+                mMediaRouter.addCallback(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY,
+                        mMediaRouterCallback, MediaRouter.CALLBACK_FLAG_PASSIVE_DISCOVERY);
+            } else {
+                Log.w(TAG, "MediaRouter not yet initialized");
+            }
             updateDisplays(true /* showing */);
         }
         mShowing = true;
@@ -172,7 +180,9 @@
     public void hide() {
         if (mShowing) {
             if (DEBUG) Log.v(TAG, "hide");
-            mMediaRouter.removeCallback(mMediaRouterCallback);
+            if (mMediaRouter != null) {
+                mMediaRouter.removeCallback(mMediaRouterCallback);
+            }
             updateDisplays(false /* showing */);
         }
         mShowing = false;
diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
index 5bd352c..676f421 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
@@ -78,6 +78,7 @@
     static final int NTWK_BLOCKED_BG_RESTRICT = 5;
     static final int NTWK_ALLOWED_DEFAULT = 6;
     static final int NTWK_ALLOWED_SYSTEM = 7;
+    static final int NTWK_BLOCKED_RESTRICTED_MODE = 8;
 
     private final LogBuffer mNetworkBlockedBuffer = new LogBuffer(MAX_NETWORK_BLOCKED_LOG_SIZE);
     private final LogBuffer mUidStateChangeBuffer = new LogBuffer(MAX_LOG_SIZE);
@@ -281,6 +282,8 @@
                 return "blocked when background is restricted";
             case NTWK_ALLOWED_DEFAULT:
                 return "allowed by default";
+            case NTWK_BLOCKED_RESTRICTED_MODE:
+                return "blocked by restricted networking mode";
             default:
                 return String.valueOf(reason);
         }
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index a486096..38ffccd 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -18,6 +18,7 @@
 
 import static android.Manifest.permission.ACCESS_NETWORK_STATE;
 import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
+import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
 import static android.Manifest.permission.MANAGE_NETWORK_POLICY;
 import static android.Manifest.permission.MANAGE_SUBSCRIPTION_PLANS;
 import static android.Manifest.permission.NETWORK_SETTINGS;
@@ -44,6 +45,7 @@
 import static android.net.ConnectivityManager.TYPE_MOBILE;
 import static android.net.INetd.FIREWALL_CHAIN_DOZABLE;
 import static android.net.INetd.FIREWALL_CHAIN_POWERSAVE;
+import static android.net.INetd.FIREWALL_CHAIN_RESTRICTED;
 import static android.net.INetd.FIREWALL_CHAIN_STANDBY;
 import static android.net.INetd.FIREWALL_RULE_ALLOW;
 import static android.net.INetd.FIREWALL_RULE_DENY;
@@ -57,6 +59,7 @@
 import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
 import static android.net.NetworkPolicyManager.MASK_ALL_NETWORKS;
 import static android.net.NetworkPolicyManager.MASK_METERED_NETWORKS;
+import static android.net.NetworkPolicyManager.MASK_RESTRICTED_MODE_NETWORKS;
 import static android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND;
 import static android.net.NetworkPolicyManager.POLICY_NONE;
 import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
@@ -65,12 +68,14 @@
 import static android.net.NetworkPolicyManager.RULE_NONE;
 import static android.net.NetworkPolicyManager.RULE_REJECT_ALL;
 import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
+import static android.net.NetworkPolicyManager.RULE_REJECT_RESTRICTED_MODE;
 import static android.net.NetworkPolicyManager.RULE_TEMPORARY_ALLOW_METERED;
 import static android.net.NetworkPolicyManager.isProcStateAllowedWhileIdleOrPowerSaveMode;
 import static android.net.NetworkPolicyManager.isProcStateAllowedWhileOnRestrictBackground;
 import static android.net.NetworkPolicyManager.resolveNetworkId;
 import static android.net.NetworkPolicyManager.uidPoliciesToString;
 import static android.net.NetworkPolicyManager.uidRulesToString;
+import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
 import static android.net.NetworkTemplate.MATCH_MOBILE;
 import static android.net.NetworkTemplate.MATCH_WIFI;
 import static android.net.NetworkTemplate.buildTemplateMobileAll;
@@ -111,6 +116,7 @@
 import static com.android.server.net.NetworkPolicyLogger.NTWK_BLOCKED_BG_RESTRICT;
 import static com.android.server.net.NetworkPolicyLogger.NTWK_BLOCKED_DENYLIST;
 import static com.android.server.net.NetworkPolicyLogger.NTWK_BLOCKED_POWER;
+import static com.android.server.net.NetworkPolicyLogger.NTWK_BLOCKED_RESTRICTED_MODE;
 import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_UPDATED;
 
 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
@@ -143,6 +149,7 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
+import android.database.ContentObserver;
 import android.net.ConnectivityManager;
 import android.net.ConnectivityManager.NetworkCallback;
 import android.net.IConnectivityManager;
@@ -442,7 +449,10 @@
     @GuardedBy("mUidRulesFirstLock") volatile boolean mRestrictPower;
     @GuardedBy("mUidRulesFirstLock") volatile boolean mDeviceIdleMode;
     // Store whether user flipped restrict background in battery saver mode
-    @GuardedBy("mUidRulesFirstLock") volatile boolean mRestrictBackgroundChangedInBsm;
+    @GuardedBy("mUidRulesFirstLock")
+    volatile boolean mRestrictBackgroundChangedInBsm;
+    @GuardedBy("mUidRulesFirstLock")
+    volatile boolean mRestrictedNetworkingMode;
 
     private final boolean mSuppressDefaultPolicy;
 
@@ -476,6 +486,8 @@
     final SparseIntArray mUidFirewallDozableRules = new SparseIntArray();
     @GuardedBy("mUidRulesFirstLock")
     final SparseIntArray mUidFirewallPowerSaveRules = new SparseIntArray();
+    @GuardedBy("mUidRulesFirstLock")
+    final SparseIntArray mUidFirewallRestrictedModeRules = new SparseIntArray();
 
     /** Set of states for the child firewall chains. True if the chain is active. */
     @GuardedBy("mUidRulesFirstLock")
@@ -595,6 +607,8 @@
     @GuardedBy("mUidRulesFirstLock")
     private final SparseBooleanArray mInternetPermissionMap = new SparseBooleanArray();
 
+    private RestrictedModeObserver mRestrictedModeObserver;
+
     // TODO: keep allowlist of system-critical services that should never have
     // rules enforced, such as system, phone, and radio UIDs.
 
@@ -608,7 +622,35 @@
         int COUNT = IS_UID_NETWORKING_BLOCKED + 1;
     }
 
-    public final StatLogger mStatLogger = new StatLogger(new String[] {
+    private static class RestrictedModeObserver extends ContentObserver {
+        private final Context mContext;
+        private final RestrictedModeListener mListener;
+
+        RestrictedModeObserver(Context ctx, RestrictedModeListener listener) {
+            super(null);
+            mContext = ctx;
+            mListener = listener;
+            mContext.getContentResolver().registerContentObserver(
+                    Settings.Global.getUriFor(Settings.Global.RESTRICTED_NETWORKING_MODE), false,
+                    this);
+        }
+
+        public boolean isRestrictedModeEnabled() {
+            return Settings.Global.getInt(mContext.getContentResolver(),
+                    Settings.Global.RESTRICTED_NETWORKING_MODE, 0) != 0;
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            mListener.onChange(isRestrictedModeEnabled());
+        }
+
+        public interface RestrictedModeListener {
+            void onChange(boolean enabled);
+        }
+    }
+
+    public final StatLogger mStatLogger = new StatLogger(new String[]{
             "updateNetworkEnabledNL()",
             "isUidNetworkingBlocked()",
     });
@@ -783,6 +825,15 @@
                     mRestrictPower = mPowerManagerInternal.getLowPowerState(
                             ServiceType.NETWORK_FIREWALL).batterySaverEnabled;
 
+                    mRestrictedModeObserver = new RestrictedModeObserver(mContext,
+                            enabled -> {
+                                synchronized (mUidRulesFirstLock) {
+                                    mRestrictedNetworkingMode = enabled;
+                                    updateRestrictedModeAllowlistUL();
+                                }
+                            });
+                    mRestrictedNetworkingMode = mRestrictedModeObserver.isRestrictedModeEnabled();
+
                     mSystemReady = true;
 
                     waitForAdminData();
@@ -3496,6 +3547,7 @@
                 fout.print("Restrict background: "); fout.println(mRestrictBackground);
                 fout.print("Restrict power: "); fout.println(mRestrictPower);
                 fout.print("Device idle: "); fout.println(mDeviceIdleMode);
+                fout.print("Restricted networking mode: "); fout.println(mRestrictedNetworkingMode);
                 synchronized (mMeteredIfacesLock) {
                     fout.print("Metered ifaces: ");
                     fout.println(mMeteredIfaces);
@@ -3808,6 +3860,93 @@
         }
     }
 
+    /**
+     * updates restricted mode state / access for all apps
+     * Called on initialization and when restricted mode is enabled / disabled.
+     */
+    @VisibleForTesting
+    @GuardedBy("mUidRulesFirstLock")
+    void updateRestrictedModeAllowlistUL() {
+        mUidFirewallRestrictedModeRules.clear();
+        forEachUid("updateRestrictedModeAllowlist", uid -> {
+            final int oldUidRule = mUidRules.get(uid);
+            final int newUidRule = getNewRestrictedModeUidRule(uid, oldUidRule);
+            final boolean hasUidRuleChanged = oldUidRule != newUidRule;
+            final int newFirewallRule = getRestrictedModeFirewallRule(newUidRule);
+
+            // setUidFirewallRulesUL will allowlist all uids that are passed to it, so only add
+            // non-default rules.
+            if (newFirewallRule != FIREWALL_RULE_DEFAULT) {
+                mUidFirewallRestrictedModeRules.append(uid, newFirewallRule);
+            }
+
+            if (hasUidRuleChanged) {
+                mUidRules.put(uid, newUidRule);
+                mHandler.obtainMessage(MSG_RULES_CHANGED, uid, newUidRule).sendToTarget();
+            }
+        });
+        if (mRestrictedNetworkingMode) {
+            // firewall rules only need to be set when this mode is being enabled.
+            setUidFirewallRulesUL(FIREWALL_CHAIN_RESTRICTED, mUidFirewallRestrictedModeRules);
+        }
+        enableFirewallChainUL(FIREWALL_CHAIN_RESTRICTED, mRestrictedNetworkingMode);
+    }
+
+    // updates restricted mode state / access for a single app / uid.
+    @VisibleForTesting
+    @GuardedBy("mUidRulesFirstLock")
+    void updateRestrictedModeForUidUL(int uid) {
+        final int oldUidRule = mUidRules.get(uid);
+        final int newUidRule = getNewRestrictedModeUidRule(uid, oldUidRule);
+        final boolean hasUidRuleChanged = oldUidRule != newUidRule;
+
+        if (hasUidRuleChanged) {
+            mUidRules.put(uid, newUidRule);
+            mHandler.obtainMessage(MSG_RULES_CHANGED, uid, newUidRule).sendToTarget();
+        }
+
+        // if restricted networking mode is on, and the app has an access exemption, the uid rule
+        // will not change, but the firewall rule will have to be updated.
+        if (mRestrictedNetworkingMode) {
+            // Note: setUidFirewallRule also updates mUidFirewallRestrictedModeRules.
+            // In this case, default firewall rules can also be added.
+            setUidFirewallRule(FIREWALL_CHAIN_RESTRICTED, uid,
+                    getRestrictedModeFirewallRule(newUidRule));
+        }
+    }
+
+    private int getNewRestrictedModeUidRule(int uid, int oldUidRule) {
+        int newRule = oldUidRule;
+        newRule &= ~MASK_RESTRICTED_MODE_NETWORKS;
+        if (mRestrictedNetworkingMode && !hasRestrictedModeAccess(uid)) {
+            newRule |= RULE_REJECT_RESTRICTED_MODE;
+        }
+        return newRule;
+    }
+
+    private static int getRestrictedModeFirewallRule(int uidRule) {
+        if ((uidRule & RULE_REJECT_RESTRICTED_MODE) != 0) {
+            // rejected in restricted mode, this is the default behavior.
+            return FIREWALL_RULE_DEFAULT;
+        } else {
+            return FIREWALL_RULE_ALLOW;
+        }
+    }
+
+    private boolean hasRestrictedModeAccess(int uid) {
+        try {
+            // TODO: this needs to be kept in sync with
+            // PermissionMonitor#hasRestrictedNetworkPermission
+            return mIPm.checkUidPermission(CONNECTIVITY_USE_RESTRICTED_NETWORKS, uid)
+                    == PERMISSION_GRANTED
+                    || mIPm.checkUidPermission(NETWORK_STACK, uid) == PERMISSION_GRANTED
+                    || mIPm.checkUidPermission(PERMISSION_MAINLINE_NETWORK_STACK, uid)
+                    == PERMISSION_GRANTED;
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
     @GuardedBy("mUidRulesFirstLock")
     void updateRulesForPowerSaveUL() {
         Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRulesForPowerSaveUL");
@@ -4029,6 +4168,7 @@
             updateRulesForAppIdleUL();
             updateRulesForRestrictPowerUL();
             updateRulesForRestrictBackgroundUL();
+            updateRestrictedModeAllowlistUL();
 
             // If the set of restricted networks may have changed, re-evaluate those.
             if (restrictedNetworksChanged) {
@@ -4245,6 +4385,7 @@
         mPowerSaveWhitelistAppIds.delete(uid);
         mPowerSaveTempWhitelistAppIds.delete(uid);
         mAppIdleTempWhitelistAppIds.delete(uid);
+        mUidFirewallRestrictedModeRules.delete(uid);
 
         // ...then update iptables asynchronously.
         mHandler.obtainMessage(MSG_RESET_FIREWALL_RULES_BY_UID, uid, 0).sendToTarget();
@@ -4270,6 +4411,10 @@
         updateRuleForAppIdleUL(uid);
         updateRuleForRestrictPowerUL(uid);
 
+        // If the uid has the necessary permissions, then it should be added to the restricted mode
+        // firewall allowlist.
+        updateRestrictedModeForUidUL(uid);
+
         // Update internal state for power-related modes.
         updateRulesForPowerRestrictionsUL(uid);
 
@@ -4995,6 +5140,8 @@
                 mUidFirewallStandbyRules.put(uid, rule);
             } else if (chain == FIREWALL_CHAIN_POWERSAVE) {
                 mUidFirewallPowerSaveRules.put(uid, rule);
+            } else if (chain == FIREWALL_CHAIN_RESTRICTED) {
+                mUidFirewallRestrictedModeRules.put(uid, rule);
             }
 
             try {
@@ -5040,6 +5187,8 @@
             mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_STANDBY, uid, FIREWALL_RULE_DEFAULT);
             mNetworkManager
                     .setFirewallUidRule(FIREWALL_CHAIN_POWERSAVE, uid, FIREWALL_RULE_DEFAULT);
+            mNetworkManager
+                    .setFirewallUidRule(FIREWALL_CHAIN_RESTRICTED, uid, FIREWALL_RULE_DEFAULT);
             mNetworkManager.setUidOnMeteredNetworkAllowlist(uid, false);
             mNetworkManager.setUidOnMeteredNetworkDenylist(uid, false);
         } catch (IllegalStateException e) {
@@ -5226,26 +5375,21 @@
         // Networks are never blocked for system components
         if (isSystem(uid)) {
             reason = NTWK_ALLOWED_SYSTEM;
-        }
-        else if (hasRule(uidRules, RULE_REJECT_ALL)) {
+        } else if (hasRule(uidRules, RULE_REJECT_RESTRICTED_MODE)) {
+            reason = NTWK_BLOCKED_RESTRICTED_MODE;
+        } else if (hasRule(uidRules, RULE_REJECT_ALL)) {
             reason = NTWK_BLOCKED_POWER;
-        }
-        else if (!isNetworkMetered) {
+        } else if (!isNetworkMetered) {
             reason = NTWK_ALLOWED_NON_METERED;
-        }
-        else if (hasRule(uidRules, RULE_REJECT_METERED)) {
+        } else if (hasRule(uidRules, RULE_REJECT_METERED)) {
             reason = NTWK_BLOCKED_DENYLIST;
-        }
-        else if (hasRule(uidRules, RULE_ALLOW_METERED)) {
+        } else if (hasRule(uidRules, RULE_ALLOW_METERED)) {
             reason = NTWK_ALLOWED_ALLOWLIST;
-        }
-        else if (hasRule(uidRules, RULE_TEMPORARY_ALLOW_METERED)) {
+        } else if (hasRule(uidRules, RULE_TEMPORARY_ALLOW_METERED)) {
             reason = NTWK_ALLOWED_TMP_ALLOWLIST;
-        }
-        else if (isBackgroundRestricted) {
+        } else if (isBackgroundRestricted) {
             reason = NTWK_BLOCKED_BG_RESTRICT;
-        }
-        else {
+        } else {
             reason = NTWK_ALLOWED_DEFAULT;
         }
 
@@ -5258,6 +5402,7 @@
             case NTWK_ALLOWED_SYSTEM:
                 blocked = false;
                 break;
+            case NTWK_BLOCKED_RESTRICTED_MODE:
             case NTWK_BLOCKED_POWER:
             case NTWK_BLOCKED_DENYLIST:
             case NTWK_BLOCKED_BG_RESTRICT:
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index 17c3b20..0320a34 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -30,7 +30,9 @@
 import android.os.Trace;
 import android.util.proto.ProtoOutputStream;
 import android.view.InsetsSource;
+import android.view.InsetsSourceControl;
 import android.view.WindowInsets;
+import android.window.TaskSnapshot;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.protolog.common.ProtoLog;
@@ -53,6 +55,26 @@
         super(source, stateController, displayContent);
     }
 
+    @Override
+    InsetsSourceControl getControl(InsetsControlTarget target) {
+        final InsetsSourceControl control = super.getControl(target);
+        if (control != null && target != null && target.getWindow() != null) {
+            final WindowState targetWin = target.getWindow();
+            // If the control target changes during the app transition with the task snapshot
+            // starting window and the IME snapshot is visible, in case not have duplicated IME
+            // showing animation during transitioning, use a flag to inform IME source control to
+            // skip showing animation once.
+            final TaskSnapshot snapshot = targetWin.getRootTask() != null
+                    ? targetWin.mWmService.getTaskSnapshot(targetWin.getRootTask().mTaskId,
+                        0 /* userId */, false /* isLowResolution */, false /* restoreFromDisk */)
+                    : null;
+            control.setSkipAnimationOnce(targetWin.mActivityRecord != null
+                    && targetWin.mActivityRecord.hasStartingWindow()
+                    && snapshot != null && snapshot.hasImeSurface());
+        }
+        return control;
+    }
+
     /**
      * Called from {@link WindowManagerInternal#showImePostLayout} when {@link InputMethodService}
      * requests to show IME on {@param imeTarget}.
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 28a99825..ff5b356 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -103,8 +103,7 @@
         mSource = source;
         mDisplayContent = displayContent;
         mStateController = stateController;
-        mFakeControl = new InsetsSourceControl(source.getType(), null /* leash */,
-                new Point());
+        mFakeControl = new InsetsSourceControl(source.getType(), null /* leash */, new Point());
 
         switch (source.getType()) {
             case ITYPE_STATUS_BAR:
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index de9fb6a..ed90cc75 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -24,7 +24,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.window.TaskSnapshot;
 import android.content.pm.PackageManager;
 import android.graphics.Bitmap;
 import android.graphics.PixelFormat;
@@ -43,6 +42,7 @@
 import android.view.WindowInsets.Type;
 import android.view.WindowInsetsController.Appearance;
 import android.view.WindowManager.LayoutParams;
+import android.window.TaskSnapshot;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.graphics.ColorUtils;
@@ -344,20 +344,20 @@
             TaskSnapshot.Builder builder) {
         Point taskSize = new Point();
         final SurfaceControl.ScreenshotHardwareBuffer taskSnapshot = createTaskSnapshot(task,
-                mHighResTaskSnapshotScale, builder.getPixelFormat(), taskSize);
+                mHighResTaskSnapshotScale, builder.getPixelFormat(), taskSize, builder);
         builder.setTaskSize(taskSize);
         return taskSnapshot;
     }
 
     @Nullable
     SurfaceControl.ScreenshotHardwareBuffer createTaskSnapshot(@NonNull Task task,
-            float scaleFraction) {
-        return createTaskSnapshot(task, scaleFraction, PixelFormat.RGBA_8888, null);
+            float scaleFraction, TaskSnapshot.Builder builder) {
+        return createTaskSnapshot(task, scaleFraction, PixelFormat.RGBA_8888, null, builder);
     }
 
     @Nullable
     SurfaceControl.ScreenshotHardwareBuffer createTaskSnapshot(@NonNull Task task,
-            float scaleFraction, int pixelFormat, Point outTaskSize) {
+            float scaleFraction, int pixelFormat, Point outTaskSize, TaskSnapshot.Builder builder) {
         if (task.getSurfaceControl() == null) {
             if (DEBUG_SCREENSHOT) {
                 Slog.w(TAG_WM, "Failed to take screenshot. No surface control for " + task);
@@ -376,6 +376,7 @@
             excludeLayers[0] = imeWindow.getSurfaceControl();
         } else {
             excludeLayers = new SurfaceControl[0];
+            builder.setHasImeSurface(imeWindow != null && imeWindow.isDrawn());
         }
         final SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
                 SurfaceControl.captureLayersExcluding(
@@ -510,7 +511,8 @@
                 hwBitmap.getColorSpace(), mainWindow.getConfiguration().orientation,
                 mainWindow.getWindowConfiguration().getRotation(), new Point(taskWidth, taskHeight),
                 contentInsets, false /* isLowResolution */, false /* isRealSnapshot */,
-                task.getWindowingMode(), getAppearance(task), false);
+                task.getWindowingMode(), getAppearance(task), false /* isTranslucent */,
+                false /* hasImeSurface */);
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotLoader.java b/services/core/java/com/android/server/wm/TaskSnapshotLoader.java
index cfdb6b3..d3bfbab 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotLoader.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotLoader.java
@@ -197,7 +197,7 @@
                     hwBitmap.getColorSpace(), proto.orientation, proto.rotation, taskSize,
                     new Rect(proto.insetLeft, proto.insetTop, proto.insetRight, proto.insetBottom),
                     loadLowResolutionBitmap, proto.isRealSnapshot, proto.windowingMode,
-                    proto.appearance, proto.isTranslucent);
+                    proto.appearance, proto.isTranslucent, false /* hasImeSurface */);
         } catch (IOException e) {
             Slog.w(TAG, "Unable to load task snapshot data for taskId=" + taskId);
             return null;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index e872c10..a8f4bae 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2927,7 +2927,7 @@
     /**
      * Returns {@code true} if this window has been shown on screen at some time in the past.
      *
-     * @deprecated Use {@link #isDrawnLw} or any of the other drawn/visibility methods.
+     * @deprecated Use {@link #isDrawn} or any of the other drawn/visibility methods.
      */
     @Deprecated
     boolean hasDrawn() {
diff --git a/services/core/jni/gnss/Utils.h b/services/core/jni/gnss/Utils.h
index 32f53d0..0938a1b 100644
--- a/services/core/jni/gnss/Utils.h
+++ b/services/core/jni/gnss/Utils.h
@@ -64,6 +64,18 @@
 }
 
 template <class T>
+jboolean checkHidlReturn(hardware::Return<sp<T>>& result, const char* errorMessage) {
+    if (!result.isOk()) {
+        logHidlError(result, errorMessage);
+        return JNI_FALSE;
+    } else if ((sp<T>)result == nullptr) {
+        return JNI_FALSE;
+    } else {
+        return JNI_TRUE;
+    }
+}
+
+template <class T>
 class JavaMethodHelper {
 public:
     // Helper function to call setter on a Java object.
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index fec0273..4db7ce2 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -16,13 +16,18 @@
 
 package com.android.server.net;
 
+import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
+import static android.Manifest.permission.NETWORK_STACK;
 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
 import static android.net.ConnectivityManager.TYPE_WIFI;
+import static android.net.INetd.FIREWALL_CHAIN_RESTRICTED;
+import static android.net.INetd.FIREWALL_RULE_ALLOW;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.NetworkPolicy.LIMIT_DISABLED;
 import static android.net.NetworkPolicy.SNOOZE_NEVER;
 import static android.net.NetworkPolicy.WARNING_DISABLED;
+import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
 import static android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND;
 import static android.net.NetworkPolicyManager.POLICY_NONE;
 import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
@@ -34,6 +39,7 @@
 import static android.net.NetworkPolicyManager.RULE_TEMPORARY_ALLOW_METERED;
 import static android.net.NetworkPolicyManager.uidPoliciesToString;
 import static android.net.NetworkPolicyManager.uidRulesToString;
+import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
 import static android.net.NetworkStats.IFACE_ALL;
 import static android.net.NetworkStats.SET_ALL;
 import static android.net.NetworkStats.TAG_ALL;
@@ -74,6 +80,7 @@
 import static org.mockito.ArgumentMatchers.isA;
 import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.mock;
@@ -97,6 +104,7 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.Signature;
+import android.content.pm.UserInfo;
 import android.net.ConnectivityManager;
 import android.net.IConnectivityManager;
 import android.net.INetworkManagementEventObserver;
@@ -123,6 +131,7 @@
 import android.os.SimpleClock;
 import android.os.SystemClock;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.platform.test.annotations.Presubmit;
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionInfo;
@@ -131,6 +140,7 @@
 import android.telephony.TelephonyManager;
 import android.test.suitebuilder.annotation.MediumTest;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.DataUnit;
 import android.util.Log;
 import android.util.Pair;
@@ -187,6 +197,7 @@
 import java.util.Iterator;
 import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.TimeZone;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutionException;
@@ -240,6 +251,7 @@
     private @Mock SubscriptionManager mSubscriptionManager;
     private @Mock CarrierConfigManager mCarrierConfigManager;
     private @Mock TelephonyManager mTelephonyManager;
+    private @Mock UserManager mUserManager;
 
     private ArgumentCaptor<ConnectivityManager.NetworkCallback> mNetworkCallbackCaptor =
             ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
@@ -351,6 +363,8 @@
                         return mNotifManager;
                     case Context.CONNECTIVITY_SERVICE:
                         return mConnectivityManager;
+                    case Context.USER_SERVICE:
+                        return mUserManager;
                     default:
                         return super.getSystemService(name);
                 }
@@ -407,11 +421,14 @@
         when(mPackageManager.getPackagesForUid(UID_B)).thenReturn(new String[] {PKG_NAME_B});
         when(mPackageManager.getPackagesForUid(UID_C)).thenReturn(new String[] {PKG_NAME_C});
         when(mPackageManager.getApplicationInfo(eq(PKG_NAME_A), anyInt()))
-                .thenReturn(buildApplicationInfo(PKG_NAME_A));
+                .thenReturn(buildApplicationInfo(PKG_NAME_A, UID_A));
         when(mPackageManager.getApplicationInfo(eq(PKG_NAME_B), anyInt()))
-                .thenReturn(buildApplicationInfo(PKG_NAME_B));
+                .thenReturn(buildApplicationInfo(PKG_NAME_B, UID_B));
         when(mPackageManager.getApplicationInfo(eq(PKG_NAME_C), anyInt()))
-                .thenReturn(buildApplicationInfo(PKG_NAME_C));
+                .thenReturn(buildApplicationInfo(PKG_NAME_C, UID_C));
+        when(mPackageManager.getInstalledApplications(anyInt())).thenReturn(
+                buildInstalledApplicationInfoList());
+        when(mUserManager.getUsers()).thenReturn(buildUserInfoList());
         when(mNetworkManager.isBandwidthControlEnabled()).thenReturn(true);
         when(mNetworkManager.setDataSaverModeEnabled(anyBoolean())).thenReturn(true);
         doNothing().when(mConnectivityManager)
@@ -1874,6 +1891,66 @@
         }
     }
 
+    private void enableRestrictedMode(boolean enable) throws Exception {
+        mService.mRestrictedNetworkingMode = enable;
+        mService.updateRestrictedModeAllowlistUL();
+        verify(mNetworkManager).setFirewallChainEnabled(FIREWALL_CHAIN_RESTRICTED,
+                enable);
+    }
+
+    @Test
+    public void testUpdateRestrictedModeAllowlist() throws Exception {
+        // initialization calls setFirewallChainEnabled, so we want to reset the invocations.
+        clearInvocations(mNetworkManager);
+        expectHasUseRestrictedNetworksPermission(UID_A, true);
+        expectHasUseRestrictedNetworksPermission(UID_B, false);
+
+        Map<Integer, Integer> firewallUidRules = new ArrayMap<>();
+        doAnswer(arg -> {
+            int[] uids = arg.getArgument(1);
+            int[] rules = arg.getArgument(2);
+            assertTrue(uids.length == rules.length);
+
+            for (int i = 0; i < uids.length; ++i) {
+                firewallUidRules.put(uids[i], rules[i]);
+            }
+            return null;
+        }).when(mNetworkManager).setFirewallUidRules(eq(FIREWALL_CHAIN_RESTRICTED),
+                any(int[].class), any(int[].class));
+
+        enableRestrictedMode(true);
+        assertEquals(FIREWALL_RULE_ALLOW, firewallUidRules.get(UID_A).intValue());
+        assertFalse(mService.isUidNetworkingBlocked(UID_A, false));
+        assertTrue(mService.isUidNetworkingBlocked(UID_B, false));
+
+        enableRestrictedMode(false);
+        assertFalse(mService.isUidNetworkingBlocked(UID_A, false));
+        assertFalse(mService.isUidNetworkingBlocked(UID_B, false));
+    }
+
+    @Test
+    public void testUpdateRestrictedModeForUid() throws Exception {
+        // initialization calls setFirewallChainEnabled, so we want to reset the invocations.
+        clearInvocations(mNetworkManager);
+        expectHasUseRestrictedNetworksPermission(UID_A, true);
+        expectHasUseRestrictedNetworksPermission(UID_B, false);
+        enableRestrictedMode(true);
+
+        // UID_D and UID_E are not part of installed applications list, so it won't have any
+        // firewall rules set yet
+        expectHasUseRestrictedNetworksPermission(UID_D, false);
+        mService.updateRestrictedModeForUidUL(UID_D);
+        verify(mNetworkManager).setFirewallUidRule(FIREWALL_CHAIN_RESTRICTED, UID_D,
+                FIREWALL_RULE_DEFAULT);
+        assertTrue(mService.isUidNetworkingBlocked(UID_D, false));
+
+        expectHasUseRestrictedNetworksPermission(UID_E, true);
+        mService.updateRestrictedModeForUidUL(UID_E);
+        verify(mNetworkManager).setFirewallUidRule(FIREWALL_CHAIN_RESTRICTED, UID_E,
+                FIREWALL_RULE_ALLOW);
+        assertFalse(mService.isUidNetworkingBlocked(UID_E, false));
+    }
+
     private String formatBlockedStateError(int uid, int rule, boolean metered,
             boolean backgroundRestricted) {
         return String.format(
@@ -1888,12 +1965,27 @@
                 .build();
     }
 
-    private ApplicationInfo buildApplicationInfo(String label) {
+    private ApplicationInfo buildApplicationInfo(String label, int uid) {
         final ApplicationInfo ai = new ApplicationInfo();
         ai.nonLocalizedLabel = label;
+        ai.uid = uid;
         return ai;
     }
 
+    private List<ApplicationInfo> buildInstalledApplicationInfoList() {
+        final List<ApplicationInfo> installedApps = new ArrayList<>();
+        installedApps.add(buildApplicationInfo(PKG_NAME_A, UID_A));
+        installedApps.add(buildApplicationInfo(PKG_NAME_B, UID_B));
+        installedApps.add(buildApplicationInfo(PKG_NAME_C, UID_C));
+        return installedApps;
+    }
+
+    private List<UserInfo> buildUserInfoList() {
+        final List<UserInfo> users = new ArrayList<>();
+        users.add(new UserInfo(USER_ID, "user1", 0));
+        return users;
+    }
+
     private NetworkInfo buildNetworkInfo() {
         final NetworkInfo ni = new NetworkInfo(ConnectivityManager.TYPE_MOBILE,
                 TelephonyManager.NETWORK_TYPE_LTE, null, null);
@@ -1967,6 +2059,15 @@
                 hasIt ? PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED);
     }
 
+    private void expectHasUseRestrictedNetworksPermission(int uid, boolean hasIt) throws Exception {
+        when(mIpm.checkUidPermission(CONNECTIVITY_USE_RESTRICTED_NETWORKS, uid)).thenReturn(
+                hasIt ? PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED);
+        when(mIpm.checkUidPermission(NETWORK_STACK, uid)).thenReturn(
+                PackageManager.PERMISSION_DENIED);
+        when(mIpm.checkUidPermission(PERMISSION_MAINLINE_NETWORK_STACK, uid)).thenReturn(
+                PackageManager.PERMISSION_DENIED);
+    }
+
     private void expectNetworkState(boolean roaming) throws Exception {
         when(mCarrierConfigManager.getConfigForSubId(eq(TEST_SUB_ID)))
                 .thenReturn(mCarrierConfig);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
index 4d37eb2..81712c6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
@@ -31,6 +31,8 @@
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.app.WindowConfiguration;
@@ -196,8 +198,37 @@
         mDisplayContent.mInputMethodWindow.setSurfaceControl(null);
         // Verify no NPE happens when calling createTaskSnapshot.
         try {
+            final TaskSnapshot.Builder builder = new TaskSnapshot.Builder();
             mWm.mTaskSnapshotController.createTaskSnapshot(mAppWindow.mActivityRecord.getTask(),
-                    1f /* scaleFraction */, PixelFormat.UNKNOWN, null /* outTaskSize */);
+                    1f /* scaleFraction */, PixelFormat.UNKNOWN, null /* outTaskSize */, builder);
+        } catch (NullPointerException e) {
+            fail("There should be no exception when calling createTaskSnapshot");
+        }
+    }
+
+    @UseTestDisplay(addWindows = {W_ACTIVITY, W_INPUT_METHOD})
+    @Test
+    public void testCreateTaskSnapshotWithIncludingIme() {
+        Task task = mAppWindow.mActivityRecord.getTask();
+        spyOn(task);
+        spyOn(mDisplayContent);
+        spyOn(mDisplayContent.mInputMethodWindow);
+        when(task.getDisplayContent().isImeAttachedToApp()).thenReturn(true);
+        // Intentionally set the IME window is in drawn state.
+        doReturn(true).when(mDisplayContent.mInputMethodWindow).isDrawn();
+        // Verify no NPE happens when calling createTaskSnapshot.
+        try {
+            final TaskSnapshot.Builder builder = new TaskSnapshot.Builder();
+            spyOn(builder);
+            mWm.mTaskSnapshotController.createTaskSnapshot(
+                    mAppWindow.mActivityRecord.getTask(), 1f /* scaleFraction */,
+                    PixelFormat.UNKNOWN, null /* outTaskSize */, builder);
+            // Verify the builder should includes IME surface.
+            verify(builder).setHasImeSurface(eq(true));
+            builder.setColorSpace(ColorSpace.get(ColorSpace.Named.SRGB));
+            builder.setTaskSize(new Point(100, 100));
+            final TaskSnapshot snapshot = builder.build();
+            assertTrue(snapshot.hasImeSurface());
         } catch (NullPointerException e) {
             fail("There should be no exception when calling createTaskSnapshot");
         }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
index d9a794a..edf7056 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
@@ -207,7 +207,8 @@
                     // is always false. Low-res snapshots are only created when loading from
                     // disk.
                     false /* isLowResolution */,
-                    mIsRealSnapshot, mWindowingMode, mSystemUiVisibility, mIsTranslucent);
+                    mIsRealSnapshot, mWindowingMode, mSystemUiVisibility, mIsTranslucent,
+                    false /* hasImeSurface */);
         }
     }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
index aa638690..eb3a5e0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
@@ -102,7 +102,7 @@
                 ColorSpace.get(ColorSpace.Named.SRGB), ORIENTATION_PORTRAIT,
                 Surface.ROTATION_0, taskSize, contentInsets, false,
                 true /* isRealSnapshot */, WINDOWING_MODE_FULLSCREEN,
-                0 /* systemUiVisibility */, false /* isTranslucent */);
+                0 /* systemUiVisibility */, false /* isTranslucent */, false /* hasImeSurface */);
     }
 
     private static TaskDescription createTaskDescription(int background, int statusBar,
diff --git a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
index 515d329..48a5ab6 100644
--- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
@@ -405,6 +405,10 @@
      */
     public static boolean checkCallingOrSelfUseIccAuthWithDeviceIdentifier(Context context,
             String callingPackage, String callingFeatureId, String message) {
+        // The implementation follows PermissionChecker.checkAppOpPermission, but it cannot be
+        // used directly: because it uses noteProxyOpNoThrow which requires the phone process
+        // having the permission, which doesn't make sense since phone process is the ower of
+        // data/action.
         // Cannot perform appop check if the calling package is null
         if (callingPackage == null) {
             return false;
@@ -413,7 +417,17 @@
         AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
         int opMode = appOps.noteOpNoThrow(AppOpsManager.OPSTR_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER,
                 callingUid, callingPackage, callingFeatureId, message);
-        return opMode == AppOpsManager.MODE_ALLOWED;
+        switch (opMode) {
+            case AppOpsManager.MODE_ALLOWED:
+            case AppOpsManager.MODE_FOREGROUND:
+                return true;
+            case AppOpsManager.MODE_DEFAULT:
+                return context.checkCallingOrSelfPermission(
+                            Manifest.permission.USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER)
+                        == PERMISSION_GRANTED;
+            default:
+                return false;
+        }
     }
 
     /**
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 30b838e..60389e1 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -9338,6 +9338,35 @@
     }
 
     /**
+     * Get the mobile provisioning url that is used to launch a browser to allow users to manage
+     * their mobile plan.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE}.
+     *
+     * TODO: The legacy design only supports single sim design. Ideally, this should support
+     * multi-sim design in current world.
+     *
+     * {@hide}
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public @Nullable String getMobileProvisioningUrl() {
+        try {
+            final ITelephony service = getITelephony();
+            if (service != null) {
+                return service.getMobileProvisioningUrl();
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            if (!isSystemProcess()) {
+                ex.rethrowAsRuntimeException();
+            }
+        }
+        return null;
+    }
+
+    /**
      * Turns mobile data on or off.
      * If this object has been created with {@link #createForSubscriptionId}, applies to the given
      * subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index de4da38..74753ca 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2369,4 +2369,10 @@
      * Gets the config of RCS VoLTE single registration enabled for the carrier/subscription.
      */
     boolean getCarrierSingleRegistrationEnabled(int subId);
+
+    /**
+     *  Return the mobile provisioning url that is used to launch a browser to allow users to manage
+     *  their mobile plan.
+     */
+    String getMobileProvisioningUrl();
 }