Merge "Use display windowing mode if undefined" into rvc-dev
diff --git a/api/test-current.txt b/api/test-current.txt
index 4eeaaf87..03f4dc5 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -3522,6 +3522,32 @@
 
 }
 
+package android.service.watchdog {
+
+  public abstract class ExplicitHealthCheckService extends android.app.Service {
+    ctor public ExplicitHealthCheckService();
+    method public final void notifyHealthCheckPassed(@NonNull String);
+    method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
+    method public abstract void onCancelHealthCheck(@NonNull String);
+    method @NonNull public abstract java.util.List<java.lang.String> onGetRequestedPackages();
+    method @NonNull public abstract java.util.List<android.service.watchdog.ExplicitHealthCheckService.PackageConfig> onGetSupportedPackages();
+    method public abstract void onRequestHealthCheck(@NonNull String);
+    method public void setCallback(@Nullable android.os.RemoteCallback);
+    field public static final String BIND_PERMISSION = "android.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE";
+    field public static final String SERVICE_INTERFACE = "android.service.watchdog.ExplicitHealthCheckService";
+  }
+
+  public static final class ExplicitHealthCheckService.PackageConfig implements android.os.Parcelable {
+    ctor public ExplicitHealthCheckService.PackageConfig(@NonNull String, long);
+    method public int describeContents();
+    method public long getHealthCheckTimeoutMillis();
+    method @NonNull public String getPackageName();
+    method public void writeToParcel(android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.service.watchdog.ExplicitHealthCheckService.PackageConfig> CREATOR;
+  }
+
+}
+
 package android.telecom {
 
   public final class Call {
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index c7dbf77..bd15264 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -8697,13 +8697,12 @@
     // Name of the package accessing app op
     optional string package_name = 2;
 
-    // operation string id per OPSTR_ constants in AppOpsManager.java
-    optional string op = 3;
+    // deprecated - set to empty string
+    optional string op_deprecated = 3 [deprecated = true];
 
     // attribution_tag; provided by developer when accessing related API, limited at 50 chars by
-    // API.
-    // Attributions must be provided through manifest using <attribution> tag available in R and
-    // above.
+    // API. Attributions must be provided through manifest using <attribution> tag available in R
+    // and above.
     optional string attribution_tag = 4;
 
     // message related to app op access, limited to 600 chars by API
@@ -8718,6 +8717,9 @@
 
     // sampling strategy used to collect this message
     optional SamplingStrategy sampling_strategy = 6;
+
+    // operation id
+    optional android.app.AppOpEnum op = 7 [default = APP_OP_NONE];
 }
 
 /*
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index 9c1fb41..b7fb280 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -1195,18 +1195,24 @@
 
     /**
      * Remove all rows that match one of specified UIDs.
+     * This mutates the original structure in place.
      * @hide
      */
     public void removeUids(int[] uids) {
-        int nextOutputEntry = 0;
-        for (int i = 0; i < size; i++) {
-            if (!ArrayUtils.contains(uids, uid[i])) {
-                maybeCopyEntry(nextOutputEntry, i);
-                nextOutputEntry++;
-            }
-        }
+        filter(e -> !ArrayUtils.contains(uids, e.uid));
+    }
 
-        size = nextOutputEntry;
+    /**
+     * Remove all rows that match one of specified UIDs.
+     * @return the result object.
+     * @hide
+     */
+    @NonNull
+    public NetworkStats removeEmptyEntries() {
+        final NetworkStats ret = this.clone();
+        ret.filter(e -> e.rxBytes != 0 || e.rxPackets != 0 || e.txBytes != 0 || e.txPackets != 0
+                || e.operations != 0);
+        return ret;
     }
 
     /**
diff --git a/core/java/android/service/watchdog/ExplicitHealthCheckService.java b/core/java/android/service/watchdog/ExplicitHealthCheckService.java
index 9950143..b1647fe 100644
--- a/core/java/android/service/watchdog/ExplicitHealthCheckService.java
+++ b/core/java/android/service/watchdog/ExplicitHealthCheckService.java
@@ -21,7 +21,9 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SdkConstant;
+import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.app.Service;
 import android.content.Intent;
 import android.content.pm.PackageManager;
@@ -64,6 +66,7 @@
  * </pre>
  * @hide
  */
+@TestApi
 @SystemApi
 public abstract class ExplicitHealthCheckService extends Service {
 
@@ -159,6 +162,15 @@
     }
 
     /**
+     * Sets {@link RemoteCallback}, for testing purpose.
+     *
+     * @hide
+     */
+    @TestApi
+    public void setCallback(@Nullable RemoteCallback callback) {
+        mCallback = callback;
+    }
+    /**
      * Implementors should call this to notify the system when explicit health check passes
      * for {@code packageName};
      */
@@ -183,6 +195,7 @@
      *
      * @hide
      */
+    @TestApi
     @SystemApi
     public static final class PackageConfig implements Parcelable {
         private static final long DEFAULT_HEALTH_CHECK_TIMEOUT_MILLIS = TimeUnit.HOURS.toMillis(1);
@@ -263,7 +276,7 @@
         }
 
         @Override
-        public void writeToParcel(Parcel parcel, int flags) {
+        public void writeToParcel(@SuppressLint({"MissingNullability"}) Parcel parcel, int flags) {
             parcel.writeString(mPackageName);
             parcel.writeLong(mHealthCheckTimeoutMillis);
         }
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index b753704..07cf415 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -25,6 +25,12 @@
       "group": "WM_DEBUG_REMOTE_ANIMATIONS",
       "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
     },
+    "-2101985723": {
+      "message": "Failed looking up window session=%s callers=%s",
+      "level": "WARN",
+      "group": "WM_ERROR",
+      "at": "com\/android\/server\/wm\/WindowManagerService.java"
+    },
     "-2072089308": {
       "message": "Attempted to add window with token that is a sub-window: %s.  Aborting.",
       "level": "WARN",
@@ -655,12 +661,6 @@
       "group": "WM_DEBUG_SCREEN_ON",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
-    "-747671114": {
-      "message": "Failed looking up window callers=%s",
-      "level": "WARN",
-      "group": "WM_ERROR",
-      "at": "com\/android\/server\/wm\/WindowManagerService.java"
-    },
     "-714291355": {
       "message": "Losing delayed focus: %s",
       "level": "INFO",
diff --git a/packages/SystemUI/res-keyguard/values-ky/strings.xml b/packages/SystemUI/res-keyguard/values-ky/strings.xml
index 022726e..d956a79 100644
--- a/packages/SystemUI/res-keyguard/values-ky/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ky/strings.xml
@@ -98,7 +98,7 @@
     <string name="kg_password_pin_failed" msgid="5136259126330604009">"SIM-картанын PIN-кодун ачуу кыйрады!"</string>
     <string name="kg_password_puk_failed" msgid="6778867411556937118">"SIM-картанын PUK-кодун ачуу кыйрады!"</string>
     <string name="kg_pin_accepted" msgid="1625501841604389716">"Код кабыл алынды!"</string>
-    <string name="keyguard_carrier_default" msgid="6359808469637388586">"Байланыш жок."</string>
+    <string name="keyguard_carrier_default" msgid="6359808469637388586">"Интернет жок."</string>
     <string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Киргизүү ыкмасын өзгөртүү"</string>
     <string name="airplane_mode" msgid="2528005343938497866">"Учак режими"</string>
     <string name="kg_prompt_reason_prepare_for_update_pin" msgid="8878724145347297575">"Жаңыртууга даярдоо үчүн PIN код талап кылынат"</string>
diff --git a/packages/SystemUI/res-keyguard/values-pl/strings.xml b/packages/SystemUI/res-keyguard/values-pl/strings.xml
index 34633d3..5f1df3e 100644
--- a/packages/SystemUI/res-keyguard/values-pl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pl/strings.xml
@@ -54,7 +54,7 @@
     <string name="keyguard_accessibility_sim_pin_area" msgid="6272116591533888062">"Miejsce na kod PIN karty SIM"</string>
     <string name="keyguard_accessibility_sim_puk_area" msgid="5537294043180237374">"Miejsce na kod PUK karty SIM"</string>
     <string name="keyguard_accessibility_next_alarm" msgid="4492876946798984630">"Następny alarm ustawiony na: <xliff:g id="ALARM">%1$s</xliff:g>"</string>
-    <string name="keyboardview_keycode_delete" msgid="8489719929424895174">"Usuwanie"</string>
+    <string name="keyboardview_keycode_delete" msgid="8489719929424895174">"Usuń"</string>
     <string name="disable_carrier_button_text" msgid="7153361131709275746">"Wyłącz eSIM"</string>
     <string name="error_disable_esim_title" msgid="3802652622784813119">"Nie można wyłączyć karty eSIM"</string>
     <string name="error_disable_esim_msg" msgid="2441188596467999327">"Nie można wyłączyć karty eSIM z powodu błędu."</string>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 6fd3bb2..4462c72 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -26,6 +26,7 @@
 import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
 import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_NAVIGATION_BARS;
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
 
 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NAV_BAR_HANDLE_FORCE_OPAQUE;
 import static com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
@@ -56,6 +57,8 @@
 import android.content.res.Configuration;
 import android.database.ContentObserver;
 import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
 import android.inputmethodservice.InputMethodService;
 import android.net.Uri;
 import android.os.Binder;
@@ -71,6 +74,7 @@
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.Display;
+import android.view.Gravity;
 import android.view.InsetsState.InternalInsetsType;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
@@ -104,6 +108,7 @@
 import com.android.systemui.recents.Recents;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.QuickStepContract;
+import com.android.systemui.shared.system.TaskStackChangeListener;
 import com.android.systemui.stackdivider.Divider;
 import com.android.systemui.statusbar.AutoHideUiElement;
 import com.android.systemui.statusbar.CommandQueue;
@@ -133,7 +138,7 @@
  * on clicks and view states of the nav bar.
  */
 public class NavigationBarFragment extends LifecycleFragment implements Callbacks,
-        NavigationModeController.ModeChangedListener {
+        NavigationModeController.ModeChangedListener, DisplayManager.DisplayListener {
 
     public static final String TAG = "NavigationBar";
     private static final boolean DEBUG = false;
@@ -141,6 +146,8 @@
     private static final String EXTRA_DISABLE2_STATE = "disabled2_state";
     private static final String EXTRA_APPEARANCE = "appearance";
     private static final String EXTRA_TRANSIENT_STATE = "transient_state";
+    private static final String FIXED_ROTATION_TRANSFORM_SETTING_NAME = "fixed_rotation_transform";
+
 
     /** Allow some time inbetween the long press for back and recents. */
     private static final int LOCK_TO_APP_GESTURE_TOLERENCE = 200;
@@ -199,6 +206,23 @@
     private boolean mIsOnDefaultDisplay;
     public boolean mHomeBlockedThisTouch;
 
+    /**
+     * When user is QuickSwitching between apps of different orientations, we'll draw a fake
+     * home handle on the orientation they originally touched down to start their swipe
+     * gesture to indicate to them that they can continue in that orientation without having to
+     * rotate the phone
+     * The secondary handle will show when we get
+     * {@link OverviewProxyListener#onQuickSwitchToNewTask(int)} callback with the
+     * original handle hidden and we'll flip the visibilities once the
+     * {@link #mTasksFrozenListener} fires
+     */
+    private NavigationHandle mOrientationHandle;
+    private WindowManager.LayoutParams mOrientationParams;
+    private boolean mFrozenTasks;
+    private int mStartingQuickSwitchRotation;
+    private int mCurrentRotation;
+    private boolean mFixedRotationEnabled;
+
     /** Only for default display */
     @Nullable
     private AssistHandleViewController mAssistHandlerViewController;
@@ -249,6 +273,12 @@
         }
 
         @Override
+        public void onQuickSwitchToNewTask(@Surface.Rotation int rotation) {
+            mStartingQuickSwitchRotation = rotation;
+            orientSecondaryHomeHandle();
+        }
+
+        @Override
         public void startAssistant(Bundle bundle) {
             mAssistManager.startAssist(bundle);
         }
@@ -271,6 +301,22 @@
         }
     };
 
+    private TaskStackChangeListener mTasksFrozenListener = new TaskStackChangeListener() {
+        @Override
+        public void onRecentTaskListFrozenChanged(boolean frozen) {
+            mFrozenTasks = frozen;
+            orientSecondaryHomeHandle();
+        }
+    };
+
+    private NavigationBarTransitions.DarkIntensityListener mOrientationHandleIntensityListener =
+            new NavigationBarTransitions.DarkIntensityListener() {
+                @Override
+                public void onDarkIntensity(float darkIntensity) {
+                    mOrientationHandle.setDarkIntensity(darkIntensity);
+                }
+            };
+
     private final ContextButtonListener mRotationButtonListener = (button, visible) -> {
         if (visible) {
             // If the button will actually become visible and the navbar is about to hide,
@@ -294,6 +340,14 @@
         }
     };
 
+    private final ContentObserver mFixedRotationObserver = new ContentObserver(
+            new Handler(Looper.getMainLooper())) {
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            updatedFixedRotation();
+        }
+    };
+
     private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener =
             new DeviceConfig.OnPropertiesChangedListener() {
         @Override
@@ -351,6 +405,10 @@
                 Settings.Secure.getUriFor(Settings.Secure.ASSISTANT),
                 false /* notifyForDescendants */, mAssistContentObserver, UserHandle.USER_ALL);
 
+        mContentResolver.registerContentObserver(
+                Settings.Global.getUriFor(FIXED_ROTATION_TRANSFORM_SETTING_NAME),
+                false /* notifyForDescendants */, mFixedRotationObserver, UserHandle.USER_ALL);
+
         if (savedInstanceState != null) {
             mDisabledFlags1 = savedInstanceState.getInt(EXTRA_DISABLE_STATE, 0);
             mDisabledFlags2 = savedInstanceState.getInt(EXTRA_DISABLE2_STATE, 0);
@@ -376,6 +434,7 @@
         mNavigationModeController.removeListener(this);
         mAccessibilityManagerWrapper.removeCallback(mAccessibilityListener);
         mContentResolver.unregisterContentObserver(mAssistContentObserver);
+        mContentResolver.unregisterContentObserver(mFixedRotationObserver);
 
         DeviceConfig.removeOnPropertiesChangedListener(mOnPropertiesChangedListener);
     }
@@ -406,6 +465,7 @@
         }
         mNavigationBarView.setNavigationIconHints(mNavigationIconHints);
         mNavigationBarView.setWindowVisible(isNavBarWindowVisible());
+        updatedFixedRotation();
 
         prepareNavigationBarView();
         checkNavBarModes();
@@ -442,6 +502,9 @@
                 new AssistHandleViewController(mHandler, mNavigationBarView);
             getBarTransitions().addDarkIntensityListener(mAssistHandlerViewController);
         }
+
+        initSecondaryHomeHandleForRotation();
+        ActivityManagerWrapper.getInstance().registerTaskStackListener(mTasksFrozenListener);
     }
 
     @Override
@@ -458,6 +521,13 @@
         }
         mOverviewProxyService.removeCallback(mOverviewProxyListener);
         mBroadcastDispatcher.unregisterReceiver(mBroadcastReceiver);
+        ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mTasksFrozenListener);
+        if (mOrientationHandle != null) {
+            resetSecondaryHandle();
+            getContext().getSystemService(DisplayManager.class).unregisterDisplayListener(this);
+            getBarTransitions().removeDarkIntensityListener(mOrientationHandleIntensityListener);
+            mWindowManager.removeView(mOrientationHandle);
+        }
     }
 
     @Override
@@ -490,6 +560,88 @@
         repositionNavigationBar();
     }
 
+    private void initSecondaryHomeHandleForRotation() {
+        if (!canShowSecondaryHandle()) {
+            return;
+        }
+
+        getContext().getSystemService(DisplayManager.class)
+                .registerDisplayListener(this, new Handler(Looper.getMainLooper()));
+
+        mOrientationHandle = new VerticalNavigationHandle(getContext());
+
+        getBarTransitions().addDarkIntensityListener(mOrientationHandleIntensityListener);
+        mOrientationParams = new WindowManager.LayoutParams(0, 0,
+                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
+                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                        | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+                        | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                        | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
+                        | WindowManager.LayoutParams.FLAG_SLIPPERY,
+                PixelFormat.TRANSLUCENT);
+        mWindowManager.addView(mOrientationHandle, mOrientationParams);
+        mOrientationHandle.setVisibility(View.GONE);
+    }
+
+    private void orientSecondaryHomeHandle() {
+        if (!canShowSecondaryHandle()) {
+            return;
+        }
+
+        if (!mFrozenTasks) {
+            resetSecondaryHandle();
+        } else {
+            int deltaRotation = deltaRotation(mCurrentRotation, mStartingQuickSwitchRotation);
+            int height = 0;
+            int width = 0;
+            Rect dispSize = mWindowManager.getCurrentWindowMetrics().getBounds();
+            switch (deltaRotation) {
+                case Surface.ROTATION_90:
+                case Surface.ROTATION_270:
+                    height = dispSize.height();
+                    width = getResources()
+                            .getDimensionPixelSize(R.dimen.navigation_bar_height);
+                    break;
+                case Surface.ROTATION_180:
+                case Surface.ROTATION_0:
+                    // TODO(b/152683657): Need to determine best UX for this
+                    resetSecondaryHandle();
+                    return;
+            }
+
+            mOrientationParams.gravity =
+                    deltaRotation == Surface.ROTATION_90 ? Gravity.LEFT : Gravity.RIGHT;
+            mOrientationParams.height = height;
+            mOrientationParams.width = width;
+            mWindowManager.updateViewLayout(mOrientationHandle, mOrientationParams);
+            mNavigationBarView.setVisibility(View.GONE);
+            mOrientationHandle.setVisibility(View.VISIBLE);
+        }
+    }
+
+    private void resetSecondaryHandle() {
+        if (mOrientationHandle != null) {
+            // Case where nav mode is changed w/o ever invoking a quickstep
+            // mOrientedHandle is initialized lazily
+            mOrientationHandle.setVisibility(View.GONE);
+        }
+        mNavigationBarView.setVisibility(View.VISIBLE);
+    }
+
+    private int deltaRotation(int oldRotation, int newRotation) {
+        int delta = newRotation - oldRotation;
+        if (delta < 0) delta += 4;
+        return delta;
+    }
+
+    private void updatedFixedRotation() {
+        mFixedRotationEnabled = Settings.Global.getInt(getContext().getContentResolver(),
+                FIXED_ROTATION_TRANSFORM_SETTING_NAME, 0) != 0;
+        if (!canShowSecondaryHandle()) {
+            resetSecondaryHandle();
+        }
+    }
+
     @Override
     public void dump(String prefix, FileDescriptor fd, PrintWriter pw, String[] args) {
         if (mNavigationBarView != null) {
@@ -1112,6 +1264,10 @@
         mNavBarMode = mode;
         updateScreenPinningGestures();
 
+        if (!canShowSecondaryHandle()) {
+            resetSecondaryHandle();
+        }
+
         // Workaround for b/132825155, for secondary users, we currently don't receive configuration
         // changes on overlay package change since SystemUI runs for the system user. In this case,
         // trigger a new configuration change to ensure that the nav bar is updated in the same way.
@@ -1156,6 +1312,34 @@
     private final AccessibilityServicesStateChangeListener mAccessibilityListener =
             this::updateAccessibilityServicesState;
 
+    @Override
+    public void onDisplayAdded(int displayId) {
+
+    }
+
+    @Override
+    public void onDisplayRemoved(int displayId) {
+
+    }
+
+    @Override
+    public void onDisplayChanged(int displayId) {
+        if (!canShowSecondaryHandle()) {
+            return;
+        }
+
+        int rotation = getContext().getResources().getConfiguration()
+                .windowConfiguration.getRotation();
+        if (rotation != mCurrentRotation) {
+            mCurrentRotation = rotation;
+            orientSecondaryHomeHandle();
+        }
+    }
+
+    private boolean canShowSecondaryHandle() {
+        return mFixedRotationEnabled && mNavBarMode == NAV_BAR_MODE_GESTURAL;
+    }
+
     private final Consumer<Integer> mRotationWatcher = rotation -> {
         if (mNavigationBarView != null
                 && mNavigationBarView.needsReorient(rotation)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationHandle.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationHandle.java
index abceb11..b874795 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationHandle.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationHandle.java
@@ -32,11 +32,11 @@
 
 public class NavigationHandle extends View implements ButtonInterface {
 
-    private final Paint mPaint = new Paint();
+    protected final Paint mPaint = new Paint();
     private @ColorInt final int mLightColor;
     private @ColorInt final int mDarkColor;
-    private final int mRadius;
-    private final int mBottom;
+    protected final int mRadius;
+    protected final int mBottom;
     private boolean mRequiresInvalidate;
 
     public NavigationHandle(Context context) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/VerticalNavigationHandle.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/VerticalNavigationHandle.java
new file mode 100644
index 0000000..a15ca95
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/VerticalNavigationHandle.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.content.Context;
+import android.graphics.Canvas;
+
+import com.android.systemui.R;
+
+/** Temporarily shown view when using QuickSwitch to switch between apps of different rotations */
+public class VerticalNavigationHandle extends NavigationHandle {
+    private final int mWidth;
+
+    public VerticalNavigationHandle(Context context) {
+        super(context);
+        mWidth = context.getResources().getDimensionPixelSize(R.dimen.navigation_home_handle_width);
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        int left;
+        int top;
+        int bottom;
+        int right;
+
+        int radiusOffset = mRadius * 2;
+        right = getWidth() - mBottom;
+        top = getHeight() / 2 - (mWidth / 2); /* (height of screen / 2) - (height of bar / 2) */
+        left = getWidth() - mBottom - radiusOffset;
+        bottom = getHeight() / 2 + (mWidth / 2);
+        canvas.drawRoundRect(left, top, right, bottom, mRadius, mRadius, mPaint);
+    }
+}
diff --git a/packages/VpnDialogs/res/values-ky/strings.xml b/packages/VpnDialogs/res/values-ky/strings.xml
index 4e2f698..23c9be8 100644
--- a/packages/VpnDialogs/res/values-ky/strings.xml
+++ b/packages/VpnDialogs/res/values-ky/strings.xml
@@ -26,7 +26,7 @@
     <string name="data_value_format" msgid="2192466557826897580">"<xliff:g id="NUMBER_0">%1$s</xliff:g> байт / <xliff:g id="NUMBER_1">%2$s</xliff:g> пакет"</string>
     <string name="always_on_disconnected_title" msgid="1906740176262776166">"Ар дайым күйүк VPN\'ге туташа албай жатат"</string>
     <string name="always_on_disconnected_message" msgid="555634519845992917">"<xliff:g id="VPN_APP_0">%1$s</xliff:g> тармагына ар дайым туташып турсун деп жөндөлгөн, бирок учурда телефонуңуз ага туташа албай жатат. <xliff:g id="VPN_APP_1">%1$s</xliff:g> тармагына кайра туташканга чейин телефонуңуз жалпыга ачык тармакты пайдаланып турат."</string>
-    <string name="always_on_disconnected_message_lockdown" msgid="4232225539869452120">"<xliff:g id="VPN_APP">%1$s</xliff:g> тармагына ар дайым туташып турсун деп жөндөлгөн, бирок учурда телефонуңуз ага туташа албай жатат. VPN тармагына кайра туташмайынча, Интернет байланышыңыз жок болот."</string>
+    <string name="always_on_disconnected_message_lockdown" msgid="4232225539869452120">"<xliff:g id="VPN_APP">%1$s</xliff:g> тармагына ар дайым туташып турсун деп жөндөлгөн, бирок учурда телефонуңуз ага туташа албай жатат. VPN тармагына кайра туташмайынча, Интернет жок болот."</string>
     <string name="always_on_disconnected_message_separator" msgid="3310614409322581371">" "</string>
     <string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"VPN жөндөөлөрүн өзгөртүү"</string>
     <string name="configure" msgid="4905518375574791375">"Конфигурациялоо"</string>
diff --git a/packages/overlays/DisplayCutoutEmulationHoleOverlay/res/values-ta/strings.xml b/packages/overlays/DisplayCutoutEmulationHoleOverlay/res/values-ta/strings.xml
new file mode 100644
index 0000000..c896ee3
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationHoleOverlay/res/values-ta/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="display_cutout_emulation_overlay" msgid="7305489596221077240">"பஞ்ச் ஹோல் கட்அவுட்"</string>
+</resources>
diff --git a/services/core/java/android/os/UserManagerInternal.java b/services/core/java/android/os/UserManagerInternal.java
index aedafbb..94f5741 100644
--- a/services/core/java/android/os/UserManagerInternal.java
+++ b/services/core/java/android/os/UserManagerInternal.java
@@ -23,6 +23,8 @@
 import android.content.pm.UserInfo;
 import android.graphics.Bitmap;
 
+import com.android.server.pm.RestrictionsSet;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -57,21 +59,18 @@
      * Called by {@link com.android.server.devicepolicy.DevicePolicyManagerService} to set
      * restrictions enforced by the user.
      *
-     * @param originatingUserId user id of the user where the restriction originated.
-     * @param restrictions a bundle of user restrictions.
-     * @param restrictionOwnerType determines which admin {@code userId} corresponds to.
-     *             The admin can be either
-     *             {@link UserManagerInternal#OWNER_TYPE_DEVICE_OWNER},
-     *             {@link UserManagerInternal#OWNER_TYPE_PROFILE_OWNER},
-     *             {@link UserManagerInternal#OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE}
-     *             or {@link UserManagerInternal#OWNER_TYPE_NO_OWNER}.
-     *             If the admin is a DEVICE_OWNER or a PROFILE_OWNER_ORG_OWNED_DEVICE then
-     *             a restriction may be applied globally depending on which restriction it is,
-     *             otherwise it will be applied just on the current user.
-     * @see OwnerType
+     * @param originatingUserId user id of the user where the restrictions originated.
+     * @param global            a bundle of global user restrictions. Global restrictions are
+     *                          restrictions that apply device-wide: to the managed profile,
+     *                          primary profile and secondary users and any profile created in
+     *                          any secondary user.
+     * @param local             a restriction set of local user restrictions. The key is the user
+     *                          id of the user whom the restrictions are targeting.
+     * @param isDeviceOwner     whether {@code originatingUserId} corresponds to device owner
+     *                          user id.
      */
     public abstract void setDevicePolicyUserRestrictions(int originatingUserId,
-            @Nullable Bundle restrictions, @OwnerType int restrictionOwnerType);
+            @Nullable Bundle global, @Nullable RestrictionsSet local, boolean isDeviceOwner);
 
     /**
      * Returns the "base" user restrictions.
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 8e6ef75..31bccea 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -5853,10 +5853,6 @@
         if (pkg == null) {
             return false;
         }
-        if (pkg.applicationInfo.targetSdkVersion <= Build.VERSION_CODES.Q) {
-            return false;
-        }
-
         String[] requestedPermissions = pkg.requestedPermissions;
         if (requestedPermissions == null) {
             return false;
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index d2443fa..323ffcf 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1716,27 +1716,6 @@
         }
     }
 
-    private void setDevicePolicyUserRestrictionsInner(@UserIdInt int originatingUserId,
-            @Nullable Bundle restrictions,
-            @UserManagerInternal.OwnerType int restrictionOwnerType) {
-        final Bundle global = new Bundle();
-        final Bundle local = new Bundle();
-
-        // Sort restrictions into local and global ensuring they don't overlap.
-        UserRestrictionsUtils.sortToGlobalAndLocal(restrictions, restrictionOwnerType, global,
-                local);
-        boolean isDeviceOwner = restrictionOwnerType == UserManagerInternal.OWNER_TYPE_DEVICE_OWNER;
-
-        RestrictionsSet localRestrictionsSet;
-        if (UserRestrictionsUtils.isEmpty(local)) {
-            localRestrictionsSet = new RestrictionsSet();
-        } else {
-            localRestrictionsSet = new RestrictionsSet(originatingUserId, local);
-        }
-        setDevicePolicyUserRestrictionsInner(originatingUserId, global, localRestrictionsSet,
-                isDeviceOwner);
-    }
-
     /**
      * See {@link UserManagerInternal#setDevicePolicyUserRestrictions}
      */
@@ -4754,10 +4733,10 @@
     private class LocalService extends UserManagerInternal {
         @Override
         public void setDevicePolicyUserRestrictions(@UserIdInt int originatingUserId,
-                @Nullable Bundle restrictions,
-                @OwnerType int restrictionOwnerType) {
+                @NonNull Bundle global, @NonNull RestrictionsSet local,
+                boolean isDeviceOwner) {
             UserManagerService.this.setDevicePolicyUserRestrictionsInner(originatingUserId,
-                    restrictions, restrictionOwnerType);
+                    global, local, isDeviceOwner);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index eec6e02..c0502b8 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -233,6 +233,13 @@
     );
 
     /**
+     * Special user restrictions that profile owner of an organization-owned managed profile can
+     * set on the parent profile instance to apply them on the personal profile.
+     */
+    private static final Set<String> PROFILE_OWNER_ORGANIZATION_OWNED_LOCAL_RESTRICTIONS =
+            Sets.newArraySet();
+
+    /**
      * User restrictions that default to {@code true} for managed profile owners.
      *
      * NB: {@link UserManager#DISALLOW_INSTALL_UNKNOWN_SOURCES} is also set by default but it is
@@ -416,7 +423,8 @@
      * @return true if a restriction is settable by profile owner of an organization owned device.
      */
     public static boolean canProfileOwnerOfOrganizationOwnedDeviceChange(String restriction) {
-        return PROFILE_OWNER_ORGANIZATION_OWNED_GLOBAL_RESTRICTIONS.contains(restriction);
+        return PROFILE_OWNER_ORGANIZATION_OWNED_GLOBAL_RESTRICTIONS.contains(restriction)
+                || PROFILE_OWNER_ORGANIZATION_OWNED_LOCAL_RESTRICTIONS.contains(restriction);
     }
 
     /**
@@ -427,31 +435,9 @@
     }
 
     /**
-     * Takes restrictions that can be set by device owner, and sort them into what should be applied
-     * globally and what should be applied only on the current user.
-     */
-    public static void sortToGlobalAndLocal(@Nullable Bundle in,
-            @UserManagerInternal.OwnerType int restrictionOwnerType, @NonNull Bundle global,
-            @NonNull Bundle local) {
-        if (in == null || in.size() == 0) {
-            return;
-        }
-        for (String key : in.keySet()) {
-            if (!in.getBoolean(key)) {
-                continue;
-            }
-            if (isGlobal(restrictionOwnerType, key)) {
-                global.putBoolean(key, true);
-            } else {
-                local.putBoolean(key, true);
-            }
-        }
-    }
-
-    /**
      * Whether given user restriction should be enforced globally.
      */
-    private static boolean isGlobal(@UserManagerInternal.OwnerType int restrictionOwnerType,
+    public static boolean isGlobal(@UserManagerInternal.OwnerType int restrictionOwnerType,
             String key) {
         return ((restrictionOwnerType == UserManagerInternal.OWNER_TYPE_DEVICE_OWNER) && (
                 PRIMARY_USER_ONLY_RESTRICTIONS.contains(key) || GLOBAL_RESTRICTIONS.contains(key)))
@@ -463,6 +449,14 @@
     }
 
     /**
+     * Whether given user restriction should be enforced locally.
+     */
+    public static boolean isLocal(@UserManagerInternal.OwnerType int restrictionOwnerType,
+            String key) {
+        return !isGlobal(restrictionOwnerType, key);
+    }
+
+    /**
      * @return true if two Bundles contain the same user restriction.
      * A null bundle and an empty bundle are considered to be equal.
      */
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 24ab89b..e9da2c4 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -3039,7 +3039,7 @@
             e.writeInt(message.getUid());
             e.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true);
             e.writeString(message.getPackageName());
-            e.writeString(message.getOp());
+            e.writeString("");
             if (message.getAttributionTag() == null) {
                 e.writeString("");
             } else {
@@ -3047,6 +3047,7 @@
             }
             e.writeString(message.getMessage());
             e.writeInt(message.getSamplingStrategy());
+            e.writeInt(AppOpsManager.strOpToOp(message.getOp()));
 
             pulledData.add(e.build());
         } catch (Throwable t) {
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index ddf166e..63952b0 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -1904,7 +1904,7 @@
                     final WallpaperData fallback =
                             new WallpaperData(wallpaper.userId, getWallpaperDir(wallpaper.userId),
                             WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
-                    ensureSaneWallpaperData(fallback, DEFAULT_DISPLAY);
+                    ensureSaneWallpaperData(fallback);
                     bindWallpaperComponentLocked(mImageWallpaper, true, false, fallback, reply);
                     mWaitingForUnlock = true;
                 }
@@ -2425,7 +2425,7 @@
         if (cropHint == null) {
             cropHint = new Rect(0, 0, 0, 0);
         } else {
-            if (cropHint.isEmpty()
+            if (cropHint.width() < 0 || cropHint.height() < 0
                     || cropHint.left < 0
                     || cropHint.top < 0) {
                 throw new IllegalArgumentException("Invalid crop rect supplied: " + cropHint);
@@ -3077,7 +3077,7 @@
                     wallpaper = new WallpaperData(userId, getWallpaperDir(userId),
                             WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
                     mLockWallpaperMap.put(userId, wallpaper);
-                    ensureSaneWallpaperData(wallpaper, DEFAULT_DISPLAY);
+                    ensureSaneWallpaperData(wallpaper);
                 } else {
                     // sanity fallback: we're in bad shape, but establishing a known
                     // valid system+lock WallpaperData will keep us from dying.
@@ -3085,7 +3085,7 @@
                     wallpaper = new WallpaperData(userId, getWallpaperDir(userId),
                             WALLPAPER, WALLPAPER_CROP);
                     mWallpaperMap.put(userId, wallpaper);
-                    ensureSaneWallpaperData(wallpaper, DEFAULT_DISPLAY);
+                    ensureSaneWallpaperData(wallpaper);
                 }
             }
         }
@@ -3196,10 +3196,10 @@
         }
 
         ensureSaneWallpaperDisplaySize(wpdData, DEFAULT_DISPLAY);
-        ensureSaneWallpaperData(wallpaper, DEFAULT_DISPLAY);
+        ensureSaneWallpaperData(wallpaper);
         WallpaperData lockWallpaper = mLockWallpaperMap.get(userId);
         if (lockWallpaper != null) {
-            ensureSaneWallpaperData(lockWallpaper, DEFAULT_DISPLAY);
+            ensureSaneWallpaperData(lockWallpaper);
         }
     }
 
@@ -3215,15 +3215,11 @@
         }
     }
 
-    private void ensureSaneWallpaperData(WallpaperData wallpaper, int displayId) {
-        final DisplayData size = getDisplayDataOrCreate(displayId);
-
-        if (displayId == DEFAULT_DISPLAY) {
-            // crop, if not previously specified
-            if (wallpaper.cropHint.width() <= 0
-                    || wallpaper.cropHint.height() <= 0) {
-                wallpaper.cropHint.set(0, 0, size.mWidth, size.mHeight);
-            }
+    private void ensureSaneWallpaperData(WallpaperData wallpaper) {
+        // Only overwrite cropHint if the rectangle is invalid.
+        if (wallpaper.cropHint.width() < 0
+                || wallpaper.cropHint.height() < 0) {
+            wallpaper.cropHint.set(0, 0, 0, 0);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index c1c8440..a66741c 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -7411,7 +7411,17 @@
      */
     boolean isResumedActivityOnDisplay() {
         final DisplayContent display = getDisplay();
-        return display != null && this == display.mTaskContainers.getResumedActivity();
+        if (display == null) {
+            return false;
+        }
+        for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
+            final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
+            final ActivityRecord resumedActivity = taskDisplayArea.getFocusedActivity();
+            if (resumedActivity != null) {
+                return resumedActivity == this;
+            }
+        }
+        return false;
     }
 
 
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index a446720..f5eba96 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -1119,8 +1119,8 @@
 
     @Override
     public boolean isAttached() {
-        final DisplayContent display = getDisplay();
-        return display != null && !display.isRemoved();
+        final TaskDisplayArea taskDisplayArea = getDisplayArea();
+        return taskDisplayArea != null && !taskDisplayArea.isRemoved();
     }
 
     // TODO: Should each user have there own stacks?
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 8af8624..02601ff 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -385,15 +385,15 @@
     private final MoveTaskToFullscreenHelper mMoveTaskToFullscreenHelper =
             new MoveTaskToFullscreenHelper();
     private class MoveTaskToFullscreenHelper {
-        private DisplayContent mToDisplay;
+        private TaskDisplayArea mToDisplayArea;
         private boolean mOnTop;
         private Task mTopTask;
         private boolean mSchedulePictureInPictureModeChange;
 
-        void process(ActivityStack fromStack, DisplayContent toDisplay, boolean onTop,
+        void process(ActivityStack fromStack, TaskDisplayArea toDisplayArea, boolean onTop,
                 boolean schedulePictureInPictureModeChange) {
             mSchedulePictureInPictureModeChange = schedulePictureInPictureModeChange;
-            mToDisplay = toDisplay;
+            mToDisplayArea = toDisplayArea;
             mOnTop = onTop;
             mTopTask = fromStack.getTopMostTask();
 
@@ -401,7 +401,7 @@
                     MoveTaskToFullscreenHelper::processLeafTask, this, PooledLambda.__(Task.class));
             fromStack.forAllLeafTasks(c, false /* traverseTopToBottom */);
             c.recycle();
-            mToDisplay = null;
+            mToDisplayArea = null;
             mTopTask = null;
         }
 
@@ -411,19 +411,18 @@
                     task.getActivityType())) {
                 final ActivityStack stack = (ActivityStack) task;
                 stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
-                if (mToDisplay.getDisplayId() != stack.getDisplayId()) {
-                    stack.reparent(mToDisplay.getDefaultTaskDisplayArea(), mOnTop);
+                if (mToDisplayArea.getDisplayId() != stack.getDisplayId()) {
+                    stack.reparent(mToDisplayArea, mOnTop);
                 } else if (mOnTop) {
-                    mToDisplay.mTaskContainers.positionStackAtTop(stack,
-                            false /* includingParents */);
+                    mToDisplayArea.positionStackAtTop(stack, false /* includingParents */);
                 } else {
-                    mToDisplay.mTaskContainers.positionStackAtBottom(stack);
+                    mToDisplayArea.positionStackAtBottom(stack);
                 }
                 return;
             }
 
-            final ActivityStack toStack = mToDisplay.mTaskContainers.getOrCreateStack(
-                    null, mTmpOptions, task, task.getActivityType(), mOnTop);
+            final ActivityStack toStack = mToDisplayArea.getOrCreateStack(null, mTmpOptions, task,
+                    task.getActivityType(), mOnTop);
             if (task == toStack) {
                 // The task was reused as the root task.
                 return;
@@ -1418,7 +1417,7 @@
                     mRootWindowContainer.getLaunchStack(null, options, task, ON_TOP);
 
             if (stack != currentStack) {
-                moveHomeStackToFrontIfNeeded(flags, stack.getDisplay(), reason);
+                moveHomeStackToFrontIfNeeded(flags, stack.getDisplayArea(), reason);
                 task.reparent(stack, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, !ANIMATE, DEFER_RESUME,
                         reason);
                 currentStack = stack;
@@ -1437,7 +1436,7 @@
         }
 
         if (!reparented) {
-            moveHomeStackToFrontIfNeeded(flags, currentStack.getDisplay(), reason);
+            moveHomeStackToFrontIfNeeded(flags, currentStack.getDisplayArea(), reason);
         }
 
         final ActivityRecord r = task.getTopNonFinishingActivity();
@@ -1451,15 +1450,16 @@
                 currentStack, forceNonResizeable);
     }
 
-    private void moveHomeStackToFrontIfNeeded(int flags, DisplayContent display, String reason) {
-        final ActivityStack focusedStack = display.getFocusedStack();
+    private void moveHomeStackToFrontIfNeeded(int flags, TaskDisplayArea taskDisplayArea,
+            String reason) {
+        final ActivityStack focusedStack = taskDisplayArea.getFocusedStack();
 
-        if ((display.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
+        if ((taskDisplayArea.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
                 && (flags & ActivityManager.MOVE_TASK_WITH_HOME) != 0)
                 || (focusedStack != null && focusedStack.isActivityTypeRecents())) {
-            // We move home stack to front when we are on a fullscreen display and caller has
+            // We move home stack to front when we are on a fullscreen display area and caller has
             // requested the home activity to move with it. Or the previous stack is recents.
-            display.mTaskContainers.moveHomeStackToFront(reason);
+            taskDisplayArea.moveHomeStackToFront(reason);
         }
     }
 
@@ -1511,15 +1511,15 @@
         mService.deferWindowLayout();
         try {
             final int windowingMode = fromStack.getWindowingMode();
-            final DisplayContent toDisplay =
-                    mRootWindowContainer.getDisplayContent(toDisplayId);
+            final TaskDisplayArea toDisplayArea = mRootWindowContainer
+                    .getDisplayContent(toDisplayId).getDefaultTaskDisplayArea();
 
             if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
                 // We are moving all tasks from the docked stack to the fullscreen stack,
                 // which is dismissing the docked stack, so resize all other stacks to
                 // fullscreen here already so we don't end up with resize trashing.
-                for (int i = toDisplay.getStackCount() - 1; i >= 0; --i) {
-                    final ActivityStack otherStack = toDisplay.getStackAt(i);
+                for (int i = toDisplayArea.getStackCount() - 1; i >= 0; --i) {
+                    final ActivityStack otherStack = toDisplayArea.getStackAt(i);
                     if (!otherStack.inSplitScreenSecondaryWindowingMode()) {
                         continue;
                     }
@@ -1535,7 +1535,7 @@
             if (fromStack.hasChild()) {
                 mTmpOptions.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN);
                 mMoveTaskToFullscreenHelper.process(
-                        fromStack, toDisplay, onTop, schedulePictureInPictureModeChange);
+                        fromStack, toDisplayArea, onTop, schedulePictureInPictureModeChange);
             }
 
             mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
@@ -1546,6 +1546,7 @@
     }
 
     void moveTasksToFullscreenStackLocked(ActivityStack fromStack, boolean onTop) {
+        // TODO(b/153089193): Support moving within the same task display area
         mWindowManager.inSurfaceTransaction(() ->
                 moveTasksToFullscreenStackInSurfaceTransaction(fromStack, DEFAULT_DISPLAY, onTop));
     }
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index 0a0049d..d777f3f 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -189,9 +189,10 @@
         mSupervisor.beginDeferResume();
         final ActivityStack homeStack;
         try {
+            // TODO(multi-display-area): Support starting home in a task display area
             // Make sure home stack exist on display.
-            homeStack = display.mTaskContainers.getOrCreateStack(WINDOWING_MODE_FULLSCREEN,
-                    ACTIVITY_TYPE_HOME, ON_TOP);
+            homeStack = display.getDefaultTaskDisplayArea().getOrCreateStack(
+                    WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
         } finally {
             mSupervisor.endDeferResume();
         }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 85517a4..7931cd5 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -2060,20 +2060,53 @@
      * activity type. Null is no compatible stack on the display.
      */
     ActivityStack getStack(int windowingMode, int activityType) {
-        return mTaskContainers.getStack(windowingMode, activityType);
+        for (int tdaNdx = getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
+            final ActivityStack stack = getTaskDisplayAreaAt(tdaNdx)
+                    .getStack(windowingMode, activityType);
+            if (stack != null) {
+                return stack;
+            }
+        }
+        return null;
+    }
+
+    protected int getTaskDisplayAreaCount() {
+        // TODO(multi-display-area): Report actual display area count
+        return 1;
+    }
+
+    protected TaskDisplayArea getTaskDisplayAreaAt(int index) {
+        // TODO(multi-display-area): Report actual display area values
+        return mTaskContainers;
+    }
+
+    ActivityStack getStack(int rootTaskId) {
+        for (int tdaNdx = getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
+            final ActivityStack stack = getTaskDisplayAreaAt(tdaNdx).getStack(rootTaskId);
+            if (stack != null) {
+                return stack;
+            }
+        }
+        return null;
     }
 
     protected int getStackCount() {
-        return mTaskContainers.mChildren.size();
-    }
-
-    protected ActivityStack getStackAt(int index) {
-        return mTaskContainers.mChildren.get(index);
+        int totalStackCount = 0;
+        for (int i = getTaskDisplayAreaCount() - 1; i >= 0; --i) {
+            totalStackCount += getTaskDisplayAreaAt(i).getStackCount();
+        }
+        return totalStackCount;
     }
 
     @VisibleForTesting
     ActivityStack getTopStack() {
-        return mTaskContainers.getTopStack();
+        for (int i = getTaskDisplayAreaCount() - 1; i >= 0; --i) {
+            final ActivityStack stack = getTaskDisplayAreaAt(i).getTopStack();
+            if (stack != null) {
+                return stack;
+            }
+        }
+        return null;
     }
 
     /**
@@ -2449,7 +2482,7 @@
             final PooledConsumer c = PooledLambda.obtainConsumer(
                     DisplayContent::processTaskForTouchExcludeRegion, this,
                     PooledLambda.__(Task.class), focusedTask, delta);
-            mTaskContainers.forAllTasks(c);
+            forAllTasks(c);
             c.recycle();
 
             // If we removed the focused task above, add it back and only leave its
@@ -2630,9 +2663,8 @@
     }
 
     void prepareFreezingTaskBounds() {
-        for (int stackNdx = mTaskContainers.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-            final ActivityStack stack = mTaskContainers.getChildAt(stackNdx);
-            stack.prepareFreezingTaskBounds();
+        for (int tdaNdx = getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
+            getTaskDisplayAreaAt(tdaNdx).prepareFreezingTaskBounds();
         }
     }
 
@@ -2738,8 +2770,8 @@
         final ActivityStack focusedStack = getFocusedStack();
         if (focusedStack != null) {
             proto.write(FOCUSED_ROOT_TASK_ID, focusedStack.getRootTaskId());
-            final ActivityRecord focusedActivity = focusedStack.getDisplay().mTaskContainers
-                    .getResumedActivity();
+            final ActivityRecord focusedActivity = focusedStack.getDisplayArea()
+                    .getFocusedActivity();
             if (focusedActivity != null) {
                 focusedActivity.writeIdentifierToProto(proto, RESUMED_ACTIVITY);
             }
@@ -2797,13 +2829,6 @@
         if (mLastFocus != mCurrentFocus) {
             pw.print("  mLastFocus="); pw.println(mLastFocus);
         }
-        if (mTaskContainers.mPreferredTopFocusableStack != null) {
-            pw.println(prefix + "mPreferredTopFocusableStack="
-                    + mTaskContainers.mPreferredTopFocusableStack);
-        }
-        if (mTaskContainers.mLastFocusedStack != null) {
-            pw.println(prefix + "mLastFocusedStack=" + mTaskContainers.mLastFocusedStack);
-        }
         if (mLosingFocus.size() > 0) {
             pw.println();
             pw.println("  Windows losing focus:");
@@ -2837,10 +2862,9 @@
         }
 
         pw.println();
-        pw.println(prefix + "Application tokens in top down Z order:");
-        for (int stackNdx = mTaskContainers.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-            final ActivityStack stack = mTaskContainers.getChildAt(stackNdx);
-            stack.dump(pw, prefix + "  ", dumpAll);
+        pw.println(prefix + "Task display areas in top down Z order:");
+        for (int tdaNdx = getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
+            getTaskDisplayAreaAt(tdaNdx).dump(pw, prefix + "  ", dumpAll);
         }
 
         pw.println();
@@ -4002,7 +4026,9 @@
         }
 
         // Initialize state of exiting applications.
-        mTaskContainers.setExitingTokensHasVisible(hasVisible);
+        for (int i = getTaskDisplayAreaCount() - 1; i >= 0; --i) {
+            getTaskDisplayAreaAt(i).setExitingTokensHasVisible(hasVisible);
+        }
     }
 
     void removeExistingTokensIfPossible() {
@@ -4014,7 +4040,9 @@
         }
 
         // Time to remove any exiting applications?
-        mTaskContainers.removeExistingAppTokensIfPossible();
+        for (int i = getTaskDisplayAreaCount() - 1; i >= 0; --i) {
+            getTaskDisplayAreaAt(i).removeExistingAppTokensIfPossible();
+        }
     }
 
     @Override
@@ -4475,7 +4503,9 @@
     }
 
     void assignStackOrdering() {
-        mTaskContainers.assignStackOrdering(getPendingTransaction());
+        for (int i = getTaskDisplayAreaCount() - 1; i >= 0; --i) {
+            getTaskDisplayAreaAt(i).assignStackOrdering(getPendingTransaction());
+        }
     }
 
     /**
@@ -5002,13 +5032,15 @@
                 || windowingMode == WINDOWING_MODE_MULTI_WINDOW);
     }
 
-    ActivityStack createStack(int windowingMode, int activityType, boolean onTop) {
-        return mTaskContainers.createStack(windowingMode, activityType, onTop, null /* info */,
-                null /* intent */, false /* createdByOrganizer */);
-    }
-
+    @Nullable
     ActivityStack getFocusedStack() {
-        return mTaskContainers.getFocusedStack();
+        for (int i = getTaskDisplayAreaCount() - 1; i >= 0; --i) {
+            final ActivityStack stack = getTaskDisplayAreaAt(i).getFocusedStack();
+            if (stack != null) {
+                return stack;
+            }
+        }
+        return null;
     }
 
     /**
@@ -5016,11 +5048,15 @@
      * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
      */
     void removeStacksInWindowingModes(int... windowingModes) {
-        mTaskContainers.removeStacksInWindowingModes(windowingModes);
+        for (int i = getTaskDisplayAreaCount() - 1; i >= 0; --i) {
+            getTaskDisplayAreaAt(i).removeStacksInWindowingModes(windowingModes);
+        }
     }
 
     void removeStacksWithActivityTypes(int... activityTypes) {
-        mTaskContainers.removeStacksWithActivityTypes(activityTypes);
+        for (int i = getTaskDisplayAreaCount() - 1; i >= 0; --i) {
+            getTaskDisplayAreaAt(i).removeStacksWithActivityTypes(activityTypes);
+        }
     }
 
     ActivityRecord topRunningActivity() {
@@ -5037,7 +5073,14 @@
      * @return The top running activity. {@code null} if none is available.
      */
     ActivityRecord topRunningActivity(boolean considerKeyguardState) {
-        return mTaskContainers.topRunningActivity(considerKeyguardState);
+        for (int i = getTaskDisplayAreaCount() - 1; i >= 0; --i) {
+            final ActivityRecord activity = getTaskDisplayAreaAt(i)
+                    .topRunningActivity(considerKeyguardState);
+            if (activity != null) {
+                return activity;
+            }
+        }
+        return null;
     }
 
     boolean updateDisplayOverrideConfigurationLocked() {
@@ -5149,7 +5192,7 @@
         // The display may be about to rotate seamlessly, and the animation of closing apps may
         // still animate in old rotation. So make sure the outdated animation won't show on the
         // rotated display.
-        mTaskContainers.forAllActivities(a -> {
+        forAllActivities(a -> {
             if (a.nowVisible && a != mFixedRotationLaunchingApp
                     && a.getWindowConfiguration().getRotation() != newRotation) {
                 final WindowContainer<?> w = a.getAnimatingContainer();
@@ -5210,40 +5253,17 @@
 
     void remove() {
         mRemoving = true;
-        final boolean destroyContentOnRemoval = shouldDestroyContentOnRemove();
         ActivityStack lastReparentedStack = null;
-        mTaskContainers.mPreferredTopFocusableStack = null;
 
-        // Stacks could be reparented from the removed display to other display. While
-        // reparenting the last stack of the removed display, the remove display is ready to be
-        // released (no more ActivityStack). But, we cannot release it at that moment or the
-        // related WindowContainer will also be removed. So, we set display as removed after
-        // reparenting stack finished.
-        final TaskDisplayArea toTaskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
         mRootWindowContainer.mStackSupervisor.beginDeferResume();
         try {
-            int numStacks = getStackCount();
-            // Keep the order from bottom to top.
-            for (int stackNdx = 0; stackNdx < numStacks; stackNdx++) {
-                final ActivityStack stack = getStackAt(stackNdx);
-                // Always finish non-standard type stacks.
-                if (destroyContentOnRemoval || !stack.isActivityTypeStandardOrUndefined()) {
-                    stack.finishAllActivitiesImmediately();
-                } else {
-                    // If default display is in split-window mode, set windowing mode of the stack
-                    // to split-screen secondary. Otherwise, set the windowing mode to undefined by
-                    // default to let stack inherited the windowing mode from the new display.
-                    final int windowingMode = toTaskDisplayArea.isSplitScreenModeActivated()
-                            ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
-                            : WINDOWING_MODE_UNDEFINED;
-                    stack.reparent(toTaskDisplayArea, true /* onTop */);
-                    stack.setWindowingMode(windowingMode);
-                    lastReparentedStack = stack;
+            int numTaskContainers = getTaskDisplayAreaCount();
+            for (int tdaNdx = 0; tdaNdx < numTaskContainers; tdaNdx++) {
+                final ActivityStack lastReparentedStackFromArea = getTaskDisplayAreaAt(tdaNdx)
+                        .remove();
+                if (lastReparentedStackFromArea != null) {
+                    lastReparentedStack = lastReparentedStackFromArea;
                 }
-                // Stacks may be removed from this display. Ensure each stack will be processed and
-                // the loop will end.
-                stackNdx -= numStacks - getStackCount();
-                numStacks = getStackCount();
             }
         } finally {
             mRootWindowContainer.mStackSupervisor.endDeferResume();
@@ -5270,12 +5290,27 @@
             return;
         }
 
-        final ActivityStack stack = getStackCount() == 1 ? getStackAt(0) : null;
-        if (stack != null && stack.isActivityTypeHome() && !stack.hasChild()) {
-            // Release this display if an empty home stack is the only thing left.
-            // Since it is the last stack, this display will be released along with the stack
-            // removal.
-            stack.removeIfPossible();
+        // Check if all task display areas have only the empty home stacks left.
+        boolean onlyEmptyHomeStacksLeft = true;
+        for (int tdaNdx = getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
+            final TaskDisplayArea taskDisplayArea = getTaskDisplayAreaAt(tdaNdx);
+            if (taskDisplayArea.getStackCount() != 1) {
+                onlyEmptyHomeStacksLeft = false;
+                break;
+            }
+            final ActivityStack stack = taskDisplayArea.getStackAt(0);
+            if (!stack.isActivityTypeHome() || stack.hasChild()) {
+                onlyEmptyHomeStacksLeft = false;
+                break;
+            }
+        }
+        if (onlyEmptyHomeStacksLeft) {
+            // Release this display if only empty home stack(s) are left. This display will be
+            // released along with the stack(s) removal.
+            for (int tdaNdx = getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
+                final ActivityStack s = getTaskDisplayAreaAt(tdaNdx).getStackAt(0);
+                s.removeIfPossible();
+            }
         } else if (getTopStack() == null) {
             removeIfPossible();
             mRootWindowContainer.mStackSupervisor
@@ -5334,10 +5369,9 @@
 
     void ensureActivitiesVisible(ActivityRecord starting, int configChanges,
             boolean preserveWindows, boolean notifyClients) {
-        for (int stackNdx = getStackCount() - 1; stackNdx >= 0; --stackNdx) {
-            final ActivityStack stack = getStackAt(stackNdx);
-            stack.ensureActivitiesVisible(starting, configChanges, preserveWindows,
-                    notifyClients);
+        for (int i = getTaskDisplayAreaCount() - 1; i >= 0; --i) {
+            getTaskDisplayAreaAt(i).ensureActivitiesVisible(starting, configChanges,
+                    preserveWindows, notifyClients);
         }
     }
 
@@ -5350,13 +5384,18 @@
     }
 
     void setDisplayToSingleTaskInstance() {
-        final int childCount = getStackCount();
-        if (childCount > 1) {
+        final int taskDisplayAreaCount = getTaskDisplayAreaCount();
+        if (taskDisplayAreaCount > 1) {
+            throw new IllegalArgumentException(
+                    "Display already has multiple task display areas. display=" + this);
+        }
+        final int stackCount = getDefaultTaskDisplayArea().getStackCount();
+        if (stackCount > 1) {
             throw new IllegalArgumentException("Display already has multiple stacks. display="
                     + this);
         }
-        if (childCount > 0) {
-            final ActivityStack stack = getStackAt(0);
+        if (stackCount > 0) {
+            final ActivityStack stack = getDefaultTaskDisplayArea().getStackAt(0);
             if (stack.getChildCount() > 1) {
                 throw new IllegalArgumentException("Display stack already has multiple tasks."
                         + " display=" + this + " stack=" + stack);
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 264da9f..caaa173 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -3247,7 +3247,7 @@
         final int dockedAppearance = updateLightStatusBarAppearanceLw(0 /* vis */,
                 mTopDockedOpaqueWindowState, mTopDockedOpaqueOrDimmingWindowState);
         final boolean inSplitScreen =
-                mService.mRoot.getDefaultDisplay().mTaskContainers.isSplitScreenModeActivated();
+                mService.mRoot.getDefaultTaskDisplayArea().isSplitScreenModeActivated();
         if (inSplitScreen) {
             mService.getStackBounds(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
                     mDockedStackBounds);
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 57a54d0..f672394 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -529,11 +529,14 @@
          * occlusion state.
          */
         private ActivityStack getStackForControllingOccluding(DisplayContent display) {
-            for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getStackAt(stackNdx);
-                if (stack != null && stack.isFocusableAndVisible()
-                        && !stack.inPinnedWindowingMode()) {
-                    return stack;
+            for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
+                final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
+                for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
+                    final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
+                    if (stack != null && stack.isFocusableAndVisible()
+                            && !stack.inPinnedWindowingMode()) {
+                        return stack;
+                    }
                 }
             }
             return null;
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 84229f0..6fda117 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -340,7 +340,7 @@
 
         // Make leashes for each of the visible/target tasks and add it to the recents animation to
         // be started
-        // TODO(multi-display-area): Support Recents on multiple task display areas
+        // TODO(b/153090560): Support Recents on multiple task display areas
         final ArrayList<Task> visibleTasks = mDisplayContent.getDefaultTaskDisplayArea()
                 .getVisibleTasks();
         final ActivityStack targetStack = mDisplayContent.getDefaultTaskDisplayArea()
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 2eeda4d..e1ef76f 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -1643,7 +1643,9 @@
             displayId = DEFAULT_DISPLAY;
         }
 
-        final ActivityRecord r = getDisplayContent(displayId).mTaskContainers.getHomeActivity();
+        // TODO(multi-display-area): Resume home on the right task container
+        final ActivityRecord r = getDisplayContent(displayId).getDefaultTaskDisplayArea()
+                .getHomeActivity();
         final String myReason = reason + " resumeHomeActivity";
 
         // Only resume home activity if isn't finishing.
@@ -1800,19 +1802,23 @@
         final ArrayList<IBinder> topActivityTokens = new ArrayList<>();
         final ActivityStack topFocusedStack = getTopDisplayFocusedStack();
         // Traverse all displays.
-        for (int i = getChildCount() - 1; i >= 0; i--) {
-            final DisplayContent display = getChildAt(i);
-            // Traverse all stacks on a display.
-            for (int j = display.getStackCount() - 1; j >= 0; --j) {
-                final ActivityStack stack = display.getStackAt(j);
-                // Get top activity from a visible stack and add it to the list.
-                if (stack.shouldBeVisible(null /* starting */)) {
-                    final ActivityRecord top = stack.getTopNonFinishingActivity();
-                    if (top != null) {
-                        if (stack == topFocusedStack) {
-                            topActivityTokens.add(0, top.appToken);
-                        } else {
-                            topActivityTokens.add(top.appToken);
+        for (int dNdx = getChildCount() - 1; dNdx >= 0; dNdx--) {
+            final DisplayContent display = getChildAt(dNdx);
+            for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
+                final TaskDisplayArea taskDisplayArea =
+                        display.getTaskDisplayAreaAt(tdaNdx);
+                // Traverse all stacks on a display area.
+                for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
+                    final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
+                    // Get top activity from a visible stack and add it to the list.
+                    if (stack.shouldBeVisible(null /* starting */)) {
+                        final ActivityRecord top = stack.getTopNonFinishingActivity();
+                        if (top != null) {
+                            if (stack == topFocusedStack) {
+                                topActivityTokens.add(0, top.appToken);
+                            } else {
+                                topActivityTokens.add(top.appToken);
+                            }
                         }
                     }
                 }
@@ -1844,10 +1850,13 @@
         // focus order.
         for (int i = getChildCount() - 1; i >= 0; --i) {
             final DisplayContent display = getChildAt(i);
-            final ActivityRecord resumedActivityOnDisplay = display.mTaskContainers
-                    .getResumedActivity();
-            if (resumedActivityOnDisplay != null) {
-                return resumedActivityOnDisplay;
+            for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
+                final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
+                final ActivityRecord resumedActivityOnTaskContainer = taskDisplayArea
+                        .getFocusedActivity();
+                if (resumedActivityOnTaskContainer != null) {
+                    return resumedActivityOnTaskContainer;
+                }
             }
         }
         return null;
@@ -1867,16 +1876,19 @@
         WindowProcessController fgApp = null;
         for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
             final DisplayContent display = getChildAt(displayNdx);
-            for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getStackAt(stackNdx);
-                if (isTopDisplayFocusedStack(stack)) {
-                    final ActivityRecord resumedActivity = stack.getResumedActivity();
-                    if (resumedActivity != null) {
-                        fgApp = resumedActivity.app;
-                    } else if (stack.mPausingActivity != null) {
-                        fgApp = stack.mPausingActivity.app;
+            for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
+                final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
+                for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
+                    final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
+                    if (isTopDisplayFocusedStack(stack)) {
+                        final ActivityRecord resumedActivity = stack.getResumedActivity();
+                        if (resumedActivity != null) {
+                            fgApp = resumedActivity.app;
+                        } else if (stack.mPausingActivity != null) {
+                            fgApp = stack.mPausingActivity.app;
+                        }
+                        break;
                     }
-                    break;
                 }
             }
         }
@@ -1992,9 +2004,12 @@
         mStackSupervisor.mStartingUsers.add(uss);
         for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
             final DisplayContent display = getChildAt(displayNdx);
-            for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getStackAt(stackNdx);
-                stack.switchUser(userId);
+            for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
+                final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
+                for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
+                    final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
+                    stack.switchUser(userId);
+                }
             }
         }
 
@@ -2196,13 +2211,17 @@
         ActivityStack focusedStack = getTopDisplayFocusedStack();
         for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
             final DisplayContent display = getChildAt(displayNdx);
-            // It is possible that request to finish activity might also remove its task and stack,
-            // so we need to be careful with indexes in the loop and check child count every time.
-            for (int stackNdx = 0; stackNdx < display.getStackCount(); ++stackNdx) {
-                final ActivityStack stack = display.getStackAt(stackNdx);
-                final Task t = stack.finishTopCrashedActivityLocked(app, reason);
-                if (stack == focusedStack || finishedTask == null) {
-                    finishedTask = t;
+            for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
+                final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
+                // It is possible that request to finish activity might also remove its task and
+                // stack, so we need to be careful with indexes in the loop and check child count
+                // every time.
+                for (int stackNdx = 0; stackNdx < display.getStackCount(); ++stackNdx) {
+                    final ActivityStack stack = taskDisplayArea.getStackAt(stackNdx);
+                    final Task t = stack.finishTopCrashedActivityLocked(app, reason);
+                    if (stack == focusedStack || finishedTask == null) {
+                        finishedTask = t;
+                    }
                 }
             }
         }
@@ -2229,26 +2248,29 @@
         for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
             boolean resumedOnDisplay = false;
             final DisplayContent display = getChildAt(displayNdx);
-            for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getStackAt(stackNdx);
-                final ActivityRecord topRunningActivity = stack.topRunningActivity();
-                if (!stack.isFocusableAndVisible() || topRunningActivity == null) {
-                    continue;
-                }
-                if (stack == targetStack) {
-                    // Simply update the result for targetStack because the targetStack had
-                    // already resumed in above. We don't want to resume it again, especially in
-                    // some cases, it would cause a second launch failure if app process was dead.
-                    resumedOnDisplay |= result;
-                    continue;
-                }
-                if (display.mTaskContainers.isTopStack(stack)
-                        && topRunningActivity.isState(RESUMED)) {
-                    // Kick off any lingering app transitions form the MoveTaskToFront operation,
-                    // but only consider the top task and stack on that display.
-                    stack.executeAppTransition(targetOptions);
-                } else {
-                    resumedOnDisplay |= topRunningActivity.makeActiveIfNeeded(target);
+            for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
+                final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
+                for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
+                    final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
+                    final ActivityRecord topRunningActivity = stack.topRunningActivity();
+                    if (!stack.isFocusableAndVisible() || topRunningActivity == null) {
+                        continue;
+                    }
+                    if (stack == targetStack) {
+                        // Simply update the result for targetStack because the targetStack had
+                        // already resumed in above. We don't want to resume it again, especially in
+                        // some cases, it would cause a second launch failure if app process was
+                        // dead.
+                        resumedOnDisplay |= result;
+                        continue;
+                    }
+                    if (taskDisplayArea.isTopStack(stack) && topRunningActivity.isState(RESUMED)) {
+                        // Kick off any lingering app transitions form the MoveTaskToFront
+                        // operation, but only consider the top task and stack on that display.
+                        stack.executeAppTransition(targetOptions);
+                    } else {
+                        resumedOnDisplay |= topRunningActivity.makeActiveIfNeeded(target);
+                    }
                 }
             }
             if (!resumedOnDisplay) {
@@ -2284,32 +2306,37 @@
             }
 
             // Set the sleeping state of the stacks on the display.
-            for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getStackAt(stackNdx);
-                if (displayShouldSleep) {
-                    stack.goToSleepIfPossible(false /* shuttingDown */);
-                } else {
-                    // When the display which can only contain one task turns on, start a special
-                    // transition. {@link AppTransitionController#handleAppTransitionReady} later
-                    // picks up the transition, and schedules
-                    // {@link ITaskStackListener#onSingleTaskDisplayDrawn} callback which is
-                    // triggered after contents are drawn on the display.
-                    if (display.isSingleTaskInstance()) {
-                        display.mDisplayContent.prepareAppTransition(
-                                TRANSIT_SHOW_SINGLE_TASK_DISPLAY, false);
-                    }
-                    stack.awakeFromSleepingLocked();
-                    if (display.isSingleTaskInstance()) {
-                        display.executeAppTransition();
-                    }
-                    if (stack.isFocusedStackOnDisplay()
-                            && !mStackSupervisor.getKeyguardController()
-                            .isKeyguardOrAodShowing(display.mDisplayId)) {
-                        // If the keyguard is unlocked - resume immediately.
-                        // It is possible that the display will not be awake at the time we
-                        // process the keyguard going away, which can happen before the sleep token
-                        // is released. As a result, it is important we resume the activity here.
-                        resumeFocusedStacksTopActivities();
+            for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
+                final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
+                for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
+                    final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
+                    if (displayShouldSleep) {
+                        stack.goToSleepIfPossible(false /* shuttingDown */);
+                    } else {
+                        // When the display which can only contain one task turns on, start a
+                        // special transition.
+                        // {@link AppTransitionController#handleAppTransitionReady} later picks up
+                        // the transition, and schedules
+                        // {@link ITaskStackListener#onSingleTaskDisplayDrawn} callback which is
+                        // triggered after contents are drawn on the display.
+                        if (display.isSingleTaskInstance()) {
+                            display.mDisplayContent.prepareAppTransition(
+                                    TRANSIT_SHOW_SINGLE_TASK_DISPLAY, false);
+                        }
+                        stack.awakeFromSleepingLocked();
+                        if (display.isSingleTaskInstance()) {
+                            display.executeAppTransition();
+                        }
+                        if (stack.isFocusedStackOnDisplay()
+                                && !mStackSupervisor.getKeyguardController()
+                                .isKeyguardOrAodShowing(display.mDisplayId)) {
+                            // If the keyguard is unlocked - resume immediately.
+                            // It is possible that the display will not be awake at the time we
+                            // process the keyguard going away, which can happen before the sleep
+                            // token is released. As a result, it is important we resume the
+                            // activity here.
+                            resumeFocusedStacksTopActivities();
+                        }
                     }
                 }
             }
@@ -2318,7 +2345,7 @@
 
     protected ActivityStack getStack(int stackId) {
         for (int i = getChildCount() - 1; i >= 0; --i) {
-            final ActivityStack stack = getChildAt(i).mTaskContainers.getStack(stackId);
+            final ActivityStack stack = getChildAt(i).getStack(stackId);
             if (stack != null) {
                 return stack;
             }
@@ -2414,9 +2441,12 @@
         if (displayId == INVALID_DISPLAY) {
             for (int displayNdx = 0; displayNdx < getChildCount(); ++displayNdx) {
                 final DisplayContent display = getChildAt(displayNdx);
-                for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
-                    final ActivityStack stack = display.getStackAt(stackNdx);
-                    list.add(getStackInfo(stack));
+                for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
+                    final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
+                    for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
+                        final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
+                        list.add(getStackInfo(stack));
+                    }
                 }
             }
             return list;
@@ -2425,9 +2455,12 @@
         if (display == null) {
             return list;
         }
-        for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
-            final ActivityStack stack = display.getStackAt(stackNdx);
-            list.add(getStackInfo(stack));
+        for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
+            final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
+            for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
+                final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
+                list.add(getStackInfo(stack));
+            }
         }
         return list;
     }
@@ -2646,19 +2679,21 @@
         boolean allSleep = true;
         for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
             final DisplayContent display = getChildAt(displayNdx);
-            for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
-                // Stacks and activities could be removed while putting activities to sleep if
-                // the app process was gone. This prevents us getting exception by accessing an
-                // invalid stack index.
-                if (stackNdx >= display.getStackCount()) {
-                    continue;
-                }
-
-                final ActivityStack stack = display.getStackAt(stackNdx);
-                if (allowDelay) {
-                    allSleep &= stack.goToSleepIfPossible(shuttingDown);
-                } else {
-                    stack.goToSleep();
+            for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
+                final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
+                for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
+                    // Stacks and activities could be removed while putting activities to sleep if
+                    // the app process was gone. This prevents us getting exception by accessing an
+                    // invalid stack index.
+                    if (sNdx >= taskDisplayArea.getStackCount()) {
+                        continue;
+                    }
+                    final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
+                    if (allowDelay) {
+                        allSleep &= stack.goToSleepIfPossible(shuttingDown);
+                    } else {
+                        stack.goToSleep();
+                    }
                 }
             }
         }
@@ -2910,14 +2945,17 @@
             windowingMode = options != null ? options.getLaunchWindowingMode()
                     : r.getWindowingMode();
         }
-        windowingMode = displayContent.mTaskContainers.validateWindowingMode(windowingMode, r,
-                candidateTask, r.getActivityType());
 
         // Return the topmost valid stack on the display.
-        for (int i = displayContent.getStackCount() - 1; i >= 0; --i) {
-            final ActivityStack stack = displayContent.getStackAt(i);
-            if (isValidLaunchStack(stack, r, windowingMode)) {
-                return stack;
+        for (int tdaNdx = displayContent.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
+            final TaskDisplayArea taskDisplayArea = displayContent.getTaskDisplayAreaAt(tdaNdx);
+            final int validatedWindowingMode = taskDisplayArea
+                    .validateWindowingMode(windowingMode, r, candidateTask, r.getActivityType());
+            for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
+                final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
+                if (isValidLaunchStack(stack, r, validatedWindowingMode)) {
+                    return stack;
+                }
             }
         }
 
@@ -3034,9 +3072,12 @@
         boolean hasVisibleActivities = false;
         for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
             final DisplayContent display = getChildAt(displayNdx);
-            for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getStackAt(stackNdx);
-                hasVisibleActivities |= stack.handleAppDied(app);
+            for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
+                final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
+                for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
+                    final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
+                    hasVisibleActivities |= stack.handleAppDied(app);
+                }
             }
         }
         return hasVisibleActivities;
@@ -3147,10 +3188,14 @@
     void finishVoiceTask(IVoiceInteractionSession session) {
         for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
             final DisplayContent display = getChildAt(displayNdx);
-            final int numStacks = display.getStackCount();
-            for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
-                final ActivityStack stack = display.getStackAt(stackNdx);
-                stack.finishVoiceTask(session);
+            int numTaskContainers = display.getTaskDisplayAreaCount();
+            for (int tdaNdx = 0; tdaNdx < numTaskContainers; tdaNdx++) {
+                final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
+                final int numStacks = display.getStackCount();
+                for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
+                    final ActivityStack stack = taskDisplayArea.getStackAt(stackNdx);
+                    stack.finishVoiceTask(session);
+                }
             }
         }
     }
@@ -3214,14 +3259,17 @@
         boolean foundResumed = false;
         for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
             final DisplayContent display = getChildAt(displayNdx);
-            for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getStackAt(stackNdx);
-                final ActivityRecord r = stack.getResumedActivity();
-                if (r != null) {
-                    if (!r.nowVisible) {
-                        return false;
+            for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
+                final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
+                for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
+                    final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
+                    final ActivityRecord r = stack.getResumedActivity();
+                    if (r != null) {
+                        if (!r.nowVisible) {
+                            return false;
+                        }
+                        foundResumed = true;
                     }
-                    foundResumed = true;
                 }
             }
         }
@@ -3232,16 +3280,19 @@
         boolean pausing = true;
         for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
             final DisplayContent display = getChildAt(displayNdx);
-            for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getStackAt(stackNdx);
-                final ActivityRecord r = stack.mPausingActivity;
-                if (r != null && !r.isState(PAUSED, STOPPED, STOPPING)) {
-                    if (DEBUG_STATES) {
-                        Slog.d(TAG_STATES,
-                                "allPausedActivitiesComplete: r=" + r + " state=" + r.getState());
-                        pausing = false;
-                    } else {
-                        return false;
+            for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
+                final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
+                for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
+                    final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
+                    final ActivityRecord r = stack.mPausingActivity;
+                    if (r != null && !r.isState(PAUSED, STOPPED, STOPPING)) {
+                        if (DEBUG_STATES) {
+                            Slog.d(TAG_STATES, "allPausedActivitiesComplete: r=" + r
+                                    + " state=" + r.getState());
+                            pausing = false;
+                        } else {
+                            return false;
+                        }
                     }
                 }
             }
@@ -3297,9 +3348,11 @@
     void cancelInitializingActivities() {
         for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
             final DisplayContent display = getChildAt(displayNdx);
-            for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getStackAt(stackNdx);
-                stack.cancelInitializingActivities();
+            for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
+                final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
+                for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
+                    taskDisplayArea.getStackAt(sNdx).cancelInitializingActivities();
+                }
             }
         }
     }
@@ -3402,20 +3455,23 @@
         }
 
         if (!sendHint) { // targetActivity != null
-            // Send power hint when the activity's process is different than the current resumed
-            // activity on all displays, or if there are no resumed activities in the system.
+            // Send power hint when the activity's process is different than the current top resumed
+            // activity on all display areas, or if there are no resumed activities in the system.
             boolean noResumedActivities = true;
             boolean allFocusedProcessesDiffer = true;
             for (int displayNdx = 0; displayNdx < getChildCount(); ++displayNdx) {
-                final DisplayContent displayContent = getChildAt(displayNdx);
-                final ActivityRecord resumedActivity = displayContent.mTaskContainers
-                        .getResumedActivity();
-                final WindowProcessController resumedActivityProcess =
-                        resumedActivity == null ? null : resumedActivity.app;
+                final DisplayContent dc = getChildAt(displayNdx);
+                for (int tdaNdx = dc.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
+                    final TaskDisplayArea taskDisplayArea = dc.getTaskDisplayAreaAt(tdaNdx);
+                    final ActivityRecord resumedActivity = taskDisplayArea.getFocusedActivity();
+                    final WindowProcessController resumedActivityProcess =
+                            resumedActivity == null ? null : resumedActivity.app;
 
-                noResumedActivities &= resumedActivityProcess == null;
-                if (resumedActivityProcess != null) {
-                    allFocusedProcessesDiffer &= !resumedActivityProcess.equals(targetActivity.app);
+                    noResumedActivities &= resumedActivityProcess == null;
+                    if (resumedActivityProcess != null) {
+                        allFocusedProcessesDiffer &= !resumedActivityProcess.equals(
+                                targetActivity.app);
+                    }
                 }
             }
             sendHint = noResumedActivities || allFocusedProcessesDiffer;
@@ -3462,10 +3518,13 @@
             int numDisplays = getChildCount();
             for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
                 final DisplayContent display = getChildAt(displayNdx);
-                for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
-                    final ActivityStack stack = display.getStackAt(stackNdx);
-                    if (!dumpVisibleStacksOnly || stack.shouldBeVisible(null)) {
-                        activities.addAll(stack.getDumpActivitiesLocked(name));
+                for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
+                    final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
+                    for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
+                        final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
+                        if (!dumpVisibleStacksOnly || stack.shouldBeVisible(null)) {
+                            activities.addAll(stack.getDumpActivitiesLocked(name));
+                        }
                     }
                 }
             }
@@ -3505,14 +3564,21 @@
             DisplayContent displayContent = getChildAt(displayNdx);
             pw.print("Display #"); pw.print(displayContent.mDisplayId);
             pw.println(" (activities from top to bottom):");
-            for (int stackNdx = displayContent.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = displayContent.getStackAt(stackNdx);
-                pw.println();
-                printed = stack.dump(fd, pw, dumpAll, dumpClient, dumpPackage, needSep);
-                needSep = printed;
+            for (int tdaNdx = displayContent.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
+                final TaskDisplayArea taskDisplayArea = displayContent.getTaskDisplayAreaAt(tdaNdx);
+                for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
+                    final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
+                    pw.println();
+                    printed = stack.dump(fd, pw, dumpAll, dumpClient, dumpPackage, needSep);
+                    needSep = printed;
+                }
             }
-            printThisActivity(pw, displayContent.mTaskContainers.getResumedActivity(), dumpPackage,
-                    needSep, " ResumedActivity:");
+            pw.println(" (resumed activities in task display areas from top to bottom):");
+            for (int tdaNdx = displayContent.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
+                final TaskDisplayArea taskDisplayArea = displayContent.getTaskDisplayAreaAt(tdaNdx);
+                printThisActivity(pw, taskDisplayArea.getFocusedActivity(), dumpPackage, needSep,
+                        "   ResumedActivity:");
+            }
         }
 
         printed |= dumpHistoryList(fd, pw, mStackSupervisor.mFinishingActivities, "  ",
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 726528c..1bc72449 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -63,6 +63,7 @@
 import com.android.internal.util.function.pooled.PooledPredicate;
 import com.android.server.protolog.common.ProtoLog;
 
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -134,6 +135,12 @@
      */
     private ArrayList<OnStackOrderChangedListener> mStackOrderChangedCallbacks = new ArrayList<>();
 
+    /**
+     * The task display area is removed from the system and we are just waiting for all activities
+     * on it to be finished before removing this object.
+     */
+    private boolean mRemoved;
+
     TaskDisplayArea(DisplayContent displayContent, WindowManagerService service) {
         super(service, Type.ANY, "TaskContainers", FEATURE_TASK_CONTAINER);
         mDisplayContent = displayContent;
@@ -990,7 +997,7 @@
         return candidate;
     }
 
-    ActivityRecord getResumedActivity() {
+    ActivityRecord getFocusedActivity() {
         final ActivityStack focusedStack = getFocusedStack();
         if (focusedStack == null) {
             return null;
@@ -1576,7 +1583,7 @@
     }
 
     boolean isRemoved() {
-        return mDisplayContent.isRemoved();
+        return mRemoved;
     }
 
     /**
@@ -1613,4 +1620,82 @@
     interface OnStackOrderChangedListener {
         void onStackOrderChanged(ActivityStack stack);
     }
+
+    void ensureActivitiesVisible(ActivityRecord starting, int configChanges,
+            boolean preserveWindows, boolean notifyClients) {
+        for (int stackNdx = getStackCount() - 1; stackNdx >= 0; --stackNdx) {
+            final ActivityStack stack = getStackAt(stackNdx);
+            stack.ensureActivitiesVisible(starting, configChanges, preserveWindows,
+                    notifyClients);
+        }
+    }
+
+    void prepareFreezingTaskBounds() {
+        for (int stackNdx = getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+            final ActivityStack stack = getChildAt(stackNdx);
+            stack.prepareFreezingTaskBounds();
+        }
+    }
+
+    /**
+     * Removes the stacks in the node applying the content removal node from the display.
+     * @return last reparented stack, or {@code null} if the stacks had to be destroyed.
+     */
+    ActivityStack remove() {
+        mPreferredTopFocusableStack = null;
+        // TODO(b/153090332): Allow setting content removal mode per task display area
+        final boolean destroyContentOnRemoval = mDisplayContent.shouldDestroyContentOnRemove();
+        final TaskDisplayArea toDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
+        ActivityStack lastReparentedStack = null;
+
+        // Stacks could be reparented from the removed display area to other display area. After
+        // reparenting the last stack of the removed display area, the display area becomes ready to
+        // be released (no more ActivityStack-s). But, we cannot release it at that moment or the
+        // related WindowContainer will also be removed. So, we set display area as removed after
+        // reparenting stack finished.
+        // Keep the order from bottom to top.
+        int numStacks = getStackCount();
+        for (int stackNdx = 0; stackNdx < numStacks; stackNdx++) {
+            final ActivityStack stack = getStackAt(stackNdx);
+            // Always finish non-standard type stacks.
+            if (destroyContentOnRemoval || !stack.isActivityTypeStandardOrUndefined()) {
+                stack.finishAllActivitiesImmediately();
+            } else {
+                // If default display is in split-window mode, set windowing mode of the
+                // stack to split-screen secondary. Otherwise, set the windowing mode to
+                // undefined by default to let stack inherited the windowing mode from the
+                // new display.
+                final int windowingMode = toDisplayArea.isSplitScreenModeActivated()
+                        ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
+                        : WINDOWING_MODE_UNDEFINED;
+                stack.reparent(toDisplayArea, true /* onTop */);
+                stack.setWindowingMode(windowingMode);
+                lastReparentedStack = stack;
+            }
+            // Stacks may be removed from this display. Ensure each stack will be processed
+            // and the loop will end.
+            stackNdx -= numStacks - getStackCount();
+            numStacks = getStackCount();
+        }
+        mRemoved = true;
+
+        return lastReparentedStack;
+    }
+
+
+    @Override
+    void dump(PrintWriter pw, String prefix, boolean dumpAll) {
+        pw.println(prefix + "TaskDisplayArea " + getName());
+        if (mPreferredTopFocusableStack != null) {
+            pw.println(prefix + "  mPreferredTopFocusableStack=" + mPreferredTopFocusableStack);
+        }
+        if (mLastFocusedStack != null) {
+            pw.println(prefix + "  mLastFocusedStack=" + mLastFocusedStack);
+        }
+        pw.println(prefix + "  Application tokens in top down Z order:");
+        for (int stackNdx = getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+            final ActivityStack stack = getChildAt(stackNdx);
+            stack.dump(pw, prefix + "    ", dumpAll);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index e6757e1..da4401a 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -705,14 +705,19 @@
     private void adjustBoundsToAvoidConflictInDisplay(@NonNull DisplayContent display,
             @NonNull Rect inOutBounds) {
         final List<Rect> taskBoundsToCheck = new ArrayList<>();
-        for (int i = 0; i < display.getStackCount(); ++i) {
-            final ActivityStack stack = display.getStackAt(i);
-            if (!stack.inFreeformWindowingMode()) {
-                continue;
-            }
+        int numTaskContainers = display.getTaskDisplayAreaCount();
+        for (int tdaNdx = 0; tdaNdx < numTaskContainers; tdaNdx++) {
+            final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
+            int numStacks = taskDisplayArea.getStackCount();
+            for (int sNdx = 0; sNdx < numStacks; ++sNdx) {
+                final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
+                if (!stack.inFreeformWindowingMode()) {
+                    continue;
+                }
 
-            for (int j = 0; j < stack.getChildCount(); ++j) {
-                taskBoundsToCheck.add(stack.getChildAt(j).getBounds());
+                for (int j = 0; j < stack.getChildCount(); ++j) {
+                    taskBoundsToCheck.add(stack.getChildAt(j).getBounds());
+                }
             }
         }
         adjustBoundsToAvoidConflict(display.getBounds(), taskBoundsToCheck, inOutBounds);
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 30fa052..75f2e1f 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -477,26 +477,27 @@
         final long origId = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
-                TaskDisplayArea taskDisplayArea =
-                        mService.mRootWindowContainer.getDisplayContent(displayId).mTaskContainers;
-                if (taskDisplayArea == null) {
+                TaskDisplayArea defaultTaskDisplayArea = mService.mRootWindowContainer
+                        .getDisplayContent(displayId).getDefaultTaskDisplayArea();
+                if (defaultTaskDisplayArea == null) {
                     return;
                 }
                 Task task = token == null
                         ? null : WindowContainer.fromBinder(token.asBinder()).asTask();
                 if (task == null) {
-                    taskDisplayArea.mLaunchRootTask = null;
+                    defaultTaskDisplayArea.mLaunchRootTask = null;
                     return;
                 }
                 if (!task.mCreatedByOrganizer) {
                     throw new IllegalArgumentException("Attempt to set task not created by "
                             + "organizer as launch root task=" + task);
                 }
-                if (task.getDisplayArea() != taskDisplayArea) {
+                if (task.getDisplayArea() == null
+                        || task.getDisplayArea().getDisplayId() != displayId) {
                     throw new RuntimeException("Can't set launch root for display " + displayId
                             + " to task on display " + task.getDisplayContent().getDisplayId());
                 }
-                taskDisplayArea.mLaunchRootTask = task;
+                task.getDisplayArea().mLaunchRootTask = task;
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -557,13 +558,16 @@
                     throw new IllegalArgumentException("Display " + displayId + " doesn't exist");
                 }
                 ArrayList<RunningTaskInfo> out = new ArrayList<>();
-                for (int i = dc.getStackCount() - 1; i >= 0; --i) {
-                    final Task task = dc.getStackAt(i);
-                    if (activityTypes != null
-                            && !ArrayUtils.contains(activityTypes, task.getActivityType())) {
-                        continue;
+                for (int tdaNdx = dc.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
+                    final TaskDisplayArea taskDisplayArea = dc.getTaskDisplayAreaAt(tdaNdx);
+                    for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
+                        final Task task = taskDisplayArea.getStackAt(sNdx);
+                        if (activityTypes != null
+                                && !ArrayUtils.contains(activityTypes, task.getActivityType())) {
+                            continue;
+                        }
+                        out.add(task.getTaskInfo());
                     }
-                    out.add(task.getTaskInfo());
                 }
                 return out;
             }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 687af64..a1e0eb7 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -5317,7 +5317,8 @@
                 throw new IllegalArgumentException(
                         "Requested window " + client + " does not exist");
             }
-            ProtoLog.w(WM_ERROR, "Failed looking up window callers=%s", Debug.getCallers(3));
+            ProtoLog.w(WM_ERROR, "Failed looking up window session=%s callers=%s", session,
+                    Debug.getCallers(3));
             return null;
         }
         if (session != null && win.mSession != session) {
@@ -5325,7 +5326,8 @@
                 throw new IllegalArgumentException("Requested window " + client + " is in session "
                         + win.mSession + ", not " + session);
             }
-            ProtoLog.w(WM_ERROR, "Failed looking up window callers=%s", Debug.getCallers(3));
+            ProtoLog.w(WM_ERROR, "Failed looking up window session=%s callers=%s", session,
+                    Debug.getCallers(3));
             return null;
         }
 
@@ -6049,11 +6051,21 @@
         pw.print("  mHasPermanentDpad="); pw.println(mHasPermanentDpad);
         mRoot.dumpTopFocusedDisplayId(pw);
         mRoot.forAllDisplays(dc -> {
+            final int displayId = dc.getDisplayId();
             final WindowState inputMethodTarget = dc.mInputMethodTarget;
             if (inputMethodTarget != null) {
-                pw.print("  mInputMethodTarget in display# "); pw.print(dc.getDisplayId());
+                pw.print("  mInputMethodTarget in display# "); pw.print(displayId);
                 pw.print(' '); pw.println(inputMethodTarget);
             }
+            if (mAccessibilityController != null) {
+                final Region magnificationRegion = new Region();
+                mAccessibilityController.getMagnificationRegionLocked(displayId,
+                        magnificationRegion);
+                pw.print("  mMagnificationRegion in display# ");
+                pw.print(displayId);
+                pw.print(' ');
+                pw.println(magnificationRegion);
+            }
         });
         pw.print("  mInTouchMode="); pw.println(mInTouchMode);
         pw.print("  mLastDisplayFreezeDuration=");
@@ -7318,7 +7330,7 @@
 
         @Override
         public boolean isStackVisibleLw(int windowingMode) {
-            // TODO(multi-display-area): Support multiple task display areas & displays
+            // TODO(b/153090332): Support multiple task display areas & displays
             final TaskDisplayArea tc = mRoot.getDefaultTaskDisplayArea();
             return tc.isStackVisible(windowingMode);
         }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 8e7585a..9baa126 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -3439,13 +3439,23 @@
         getMergedConfiguration(mLastReportedConfiguration);
         mLastConfigReportedToClient = true;
 
+        final boolean reportOrientation = mReportOrientationChanged;
+        // Always reset these states first, so if {@link IWindow#resized} fails, this
+        // window won't be added to {@link WindowManagerService#mResizingWindows} and set
+        // {@link #mOrientationChanging} to true again by {@link #updateResizingWindowIfNeeded}
+        // that may cause WINDOW_FREEZE_TIMEOUT because resizing the client keeps failing.
+        mReportOrientationChanged = false;
+        mDragResizingChangeReported = true;
+        mWinAnimator.mSurfaceResized = false;
+        mWindowFrames.resetInsetsChanged();
+
         final Rect frame = mWindowFrames.mCompatFrame;
         final Rect contentInsets = mWindowFrames.mLastContentInsets;
         final Rect visibleInsets = mWindowFrames.mLastVisibleInsets;
         final Rect stableInsets = mWindowFrames.mLastStableInsets;
         final MergedConfiguration mergedConfiguration = mLastReportedConfiguration;
         final boolean reportDraw = mWinAnimator.mDrawState == DRAW_PENDING;
-        final boolean forceRelayout = mReportOrientationChanged || isDragResizeChanged();
+        final boolean forceRelayout = reportOrientation || isDragResizeChanged();
         final int displayId = getDisplayId();
         final DisplayCutout displayCutout = getWmDisplayCutout().getDisplayCutout();
 
@@ -3454,25 +3464,17 @@
                     mergedConfiguration, getBackdropFrame(frame), forceRelayout,
                     getDisplayContent().getDisplayPolicy().areSystemBarsForcedShownLw(this),
                     displayId, new DisplayCutout.ParcelableWrapper(displayCutout));
-            mDragResizingChangeReported = true;
 
             if (mWmService.mAccessibilityController != null) {
                 mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(displayId);
             }
             updateLocationInParentDisplayIfNeeded();
-
-            mWindowFrames.resetInsetsChanged();
-            mWinAnimator.mSurfaceResized = false;
-            mReportOrientationChanged = false;
         } catch (RemoteException e) {
+            // Cancel orientation change of this window to avoid blocking unfreeze display.
             setOrientationChanging(false);
             mLastFreezeDuration = (int)(SystemClock.elapsedRealtime()
                     - mWmService.mDisplayFreezeTime);
-            // We are assuming the hosting process is dead or in a zombie state.
-            Slog.w(TAG, "Failed to report 'resized' to the client of " + this
-                    + ", removing this window.");
-            mWmService.mPendingRemove.add(this);
-            mWmService.mWindowPlacerLocked.requestTraversal();
+            Slog.w(TAG, "Failed to report 'resized' to " + this + " due to " + e);
         }
         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
     }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index eed39e1..d1c47d9 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -87,6 +87,9 @@
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
 import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
 import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
+import static android.os.UserManagerInternal.OWNER_TYPE_DEVICE_OWNER;
+import static android.os.UserManagerInternal.OWNER_TYPE_PROFILE_OWNER;
+import static android.os.UserManagerInternal.OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE;
 import static android.provider.Settings.Global.PRIVATE_DNS_MODE;
 import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER;
 import static android.provider.Telephony.Carriers.DPC_URI;
@@ -284,6 +287,7 @@
 import com.android.server.devicepolicy.DevicePolicyManagerService.ActiveAdmin.TrustAgentInfo;
 import com.android.server.inputmethod.InputMethodManagerInternal;
 import com.android.server.net.NetworkPolicyManagerInternal;
+import com.android.server.pm.RestrictionsSet;
 import com.android.server.pm.UserRestrictionsUtils;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.storage.DeviceStorageMonitorInternal;
@@ -322,6 +326,7 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Function;
+import java.util.function.Predicate;
 
 /**
  * Implementation of the device policy APIs.
@@ -1828,6 +1833,50 @@
             info = deviceAdminInfo;
         }
 
+        Bundle addSyntheticRestrictions(Bundle restrictions) {
+            if (disableCamera) {
+                restrictions.putBoolean(UserManager.DISALLOW_CAMERA, true);
+            } else {
+                restrictions.remove(UserManager.DISALLOW_CAMERA);
+            }
+            return restrictions;
+        }
+
+        static Bundle removeDeprecatedRestrictions(Bundle restrictions) {
+            for (String deprecatedRestriction: DEPRECATED_USER_RESTRICTIONS) {
+                restrictions.remove(deprecatedRestriction);
+            }
+            return restrictions;
+        }
+
+        static Bundle filterRestrictions(Bundle restrictions, Predicate<String> filter) {
+            Bundle result = new Bundle();
+            for (String key : restrictions.keySet()) {
+                if (!restrictions.getBoolean(key)) {
+                    continue;
+                }
+                if (filter.test(key)) {
+                    result.putBoolean(key, true);
+                }
+            }
+            return result;
+        }
+
+        Bundle getEffectiveRestrictions() {
+            return addSyntheticRestrictions(
+                    removeDeprecatedRestrictions(ensureUserRestrictions()));
+        }
+
+        Bundle getLocalUserRestrictions(int adminType) {
+            return filterRestrictions(getEffectiveRestrictions(),
+                    key -> UserRestrictionsUtils.isLocal(adminType, key));
+        }
+
+        Bundle getGlobalUserRestrictions(int adminType) {
+            return filterRestrictions(getEffectiveRestrictions(),
+                    key -> UserRestrictionsUtils.isGlobal(adminType, key));
+        }
+
         void dump(IndentingPrintWriter pw) {
             pw.print("uid="); pw.println(getUid());
             pw.print("testOnlyAdmin=");
@@ -2772,7 +2821,7 @@
                 Settings.Secure.UNKNOWN_SOURCES_DEFAULT_REVERSED, 0, userId) != 0) {
             profileOwner.ensureUserRestrictions().putBoolean(
                     UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, true);
-            saveUserRestrictionsLocked(userId, /* parent = */ false);
+            saveUserRestrictionsLocked(userId);
             mInjector.settingsSecurePutIntForUser(
                     Settings.Secure.UNKNOWN_SOURCES_DEFAULT_REVERSED, 0, userId);
         }
@@ -2803,7 +2852,7 @@
             }
             admin.defaultEnabledRestrictionsAlreadySet.addAll(restrictionsToSet);
             Slog.i(LOG_TAG, "Enabled the following restrictions by default: " + restrictionsToSet);
-            saveUserRestrictionsLocked(userId, /* parent = */ false);
+            saveUserRestrictionsLocked(userId);
         }
     }
 
@@ -8222,9 +8271,9 @@
             }
         }
         // Tell the user manager that the restrictions have changed.
-        final int affectedUserId = parent ? getProfileParentId(userHandle) : userHandle;
-        pushUserRestrictions(affectedUserId);
+        pushUserRestrictions(userHandle);
 
+        final int affectedUserId = parent ? getProfileParentId(userHandle) : userHandle;
         if (SecurityLog.isLoggingEnabled()) {
             SecurityLog.writeEvent(SecurityLog.TAG_CAMERA_POLICY_SET,
                     who.getPackageName(), userHandle, affectedUserId, disabled ? 1 : 0);
@@ -10806,10 +10855,14 @@
                             "Cannot use the parent instance in Device Owner mode");
                 }
             } else {
-                if (!(UserRestrictionsUtils.canProfileOwnerChange(key, userHandle) || (
-                        isProfileOwnerOfOrganizationOwnedDevice(activeAdmin) && parent
-                        && UserRestrictionsUtils.canProfileOwnerOfOrganizationOwnedDeviceChange(
-                                key)))) {
+                boolean profileOwnerCanChangeOnItself = !parent
+                        && UserRestrictionsUtils.canProfileOwnerChange(key, userHandle);
+                boolean orgOwnedProfileOwnerCanChangesGlobally = parent
+                        && isProfileOwnerOfOrganizationOwnedDevice(activeAdmin)
+                        && UserRestrictionsUtils
+                                .canProfileOwnerOfOrganizationOwnedDeviceChange(key);
+
+                if (!profileOwnerCanChangeOnItself && !orgOwnedProfileOwnerCanChangesGlobally) {
                     throw new SecurityException("Profile owner cannot set user restriction " + key);
                 }
             }
@@ -10821,7 +10874,7 @@
             } else {
                 restrictions.remove(key);
             }
-            saveUserRestrictionsLocked(userHandle, parent);
+            saveUserRestrictionsLocked(userHandle);
         }
         final int eventId = enabledFromThisOwner
                 ? DevicePolicyEnums.ADD_USER_RESTRICTION
@@ -10839,91 +10892,65 @@
         }
     }
 
-    private void saveUserRestrictionsLocked(int userId, boolean parent) {
+    private void saveUserRestrictionsLocked(int userId) {
         saveSettingsLocked(userId);
-        pushUserRestrictions(parent ? getProfileParentId(userId) : userId);
+        pushUserRestrictions(userId);
         sendChangedNotification(userId);
     }
 
-    private void pushUserRestrictions(int userId) {
+    /**
+     * Pushes the user restrictions originating from a specific user.
+     *
+     * If called by the profile owner of an organization-owned device, the global and local
+     * user restrictions will be an accumulation of the global user restrictions from the profile
+     * owner active admin and its parent active admin. The key of the local user restrictions set
+     * will be the target user id.
+     */
+    private void pushUserRestrictions(int originatingUserId) {
+        final Bundle global;
+        final RestrictionsSet local = new RestrictionsSet();
+        final boolean isDeviceOwner;
         synchronized (getLockObject()) {
-            final boolean isDeviceOwner = mOwners.isDeviceOwnerUserId(userId);
-            Bundle userRestrictions = null;
-            final int restrictionOwnerType;
-            final int originatingUserId;
-
+            isDeviceOwner = mOwners.isDeviceOwnerUserId(originatingUserId);
             if (isDeviceOwner) {
                 final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
                 if (deviceOwner == null) {
                     return; // Shouldn't happen.
                 }
-                userRestrictions = addOrRemoveDisableCameraRestriction(
-                        deviceOwner.userRestrictions, deviceOwner);
-                restrictionOwnerType = UserManagerInternal.OWNER_TYPE_DEVICE_OWNER;
-                originatingUserId = deviceOwner.getUserHandle().getIdentifier();
+                global = deviceOwner.getGlobalUserRestrictions(OWNER_TYPE_DEVICE_OWNER);
+                local.updateRestrictions(originatingUserId, deviceOwner.getLocalUserRestrictions(
+                        OWNER_TYPE_DEVICE_OWNER));
             } else {
-                final ActiveAdmin profileOwnerOfOrganizationOwnedDevice =
-                        getProfileOwnerOfOrganizationOwnedDeviceLocked(userId);
-
-                // If profile owner of an organization owned device, the restrictions will be
-                // pushed to the parent instance.
-                if (profileOwnerOfOrganizationOwnedDevice != null && !isManagedProfile(userId)) {
-                    restrictionOwnerType =
-                          UserManagerInternal.OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE;
-                    final ActiveAdmin parent = profileOwnerOfOrganizationOwnedDevice
-                            .getParentActiveAdmin();
-                    userRestrictions = parent.userRestrictions;
-                    userRestrictions = addOrRemoveDisableCameraRestriction(userRestrictions,
-                            parent);
-                    originatingUserId =
-                            profileOwnerOfOrganizationOwnedDevice.getUserHandle().getIdentifier();
-                } else {
-                    final ActiveAdmin profileOwner = getProfileOwnerAdminLocked(userId);
-
-                    if (profileOwner != null) {
-                        userRestrictions = profileOwner.userRestrictions;
-                        restrictionOwnerType = UserManagerInternal.OWNER_TYPE_PROFILE_OWNER;
-                        originatingUserId = profileOwner.getUserHandle().getIdentifier();
-                    } else {
-                        restrictionOwnerType = UserManagerInternal.OWNER_TYPE_NO_OWNER;
-                        originatingUserId = userId;
-                    }
-                    userRestrictions = addOrRemoveDisableCameraRestriction(
-                            userRestrictions, userId);
+                final ActiveAdmin profileOwner = getProfileOwnerAdminLocked(originatingUserId);
+                if (profileOwner == null) {
+                    return;
+                }
+                global = profileOwner.getGlobalUserRestrictions(OWNER_TYPE_PROFILE_OWNER);
+                local.updateRestrictions(originatingUserId, profileOwner.getLocalUserRestrictions(
+                        OWNER_TYPE_PROFILE_OWNER));
+                // Global (device-wide) and local user restrictions set by the profile owner of an
+                // organization-owned device are stored in the parent ActiveAdmin instance.
+                if (isProfileOwnerOfOrganizationOwnedDevice(
+                        profileOwner.getUserHandle().getIdentifier())) {
+                    // The global restrictions set on the parent ActiveAdmin instance need to be
+                    // merged with the global restrictions set on the profile owner ActiveAdmin
+                    // instance, since both are to be applied device-wide.
+                    UserRestrictionsUtils.merge(global,
+                            profileOwner.getParentActiveAdmin().getGlobalUserRestrictions(
+                                    OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE));
+                    // The local restrictions set on the parent ActiveAdmin instance are only to be
+                    // applied to the primary user. They therefore need to be added the local
+                    // restriction set with the primary user id as the key, in this case the
+                    // primary user id is the target user.
+                    local.updateRestrictions(
+                            getProfileParentId(profileOwner.getUserHandle().getIdentifier()),
+                            profileOwner.getParentActiveAdmin().getLocalUserRestrictions(
+                                    OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE));
                 }
             }
-            // Remove deprecated restrictions.
-            for (String deprecatedRestriction: DEPRECATED_USER_RESTRICTIONS) {
-                userRestrictions.remove(deprecatedRestriction);
-            }
-            mUserManagerInternal.setDevicePolicyUserRestrictions(originatingUserId,
-                    userRestrictions, restrictionOwnerType);
         }
-    }
-
-    private Bundle addOrRemoveDisableCameraRestriction(Bundle userRestrictions, ActiveAdmin admin) {
-        if (userRestrictions == null) {
-            userRestrictions = new Bundle();
-        }
-        if (admin.disableCamera) {
-            userRestrictions.putBoolean(UserManager.DISALLOW_CAMERA, true);
-        } else {
-            userRestrictions.remove(UserManager.DISALLOW_CAMERA);
-        }
-        return userRestrictions;
-    }
-
-    private Bundle addOrRemoveDisableCameraRestriction(Bundle userRestrictions, int userId) {
-        if (userRestrictions == null) {
-            userRestrictions = new Bundle();
-        }
-        if (getCameraDisabled(/* who= */ null, userId, /* mergeDeviceOwnerRestriction= */
-                false)) {
-            userRestrictions.putBoolean(UserManager.DISALLOW_CAMERA, true);
-        } else {
-            userRestrictions.remove(UserManager.DISALLOW_CAMERA);
-        }
-        return userRestrictions;
+        mUserManagerInternal.setDevicePolicyUserRestrictions(originatingUserId, global, local,
+                isDeviceOwner);
     }
 
     @Override
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index fe47cea..d780370 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -83,7 +83,6 @@
 import android.os.Process;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.os.UserManagerInternal;
 import android.platform.test.annotations.Presubmit;
 import android.provider.Settings;
 import android.security.KeyChain;
@@ -1170,7 +1169,6 @@
                 () -> dpm.clearDeviceOwnerApp(admin1.getPackageName()));
 
         when(getServices().userManager.isUserUnlocked(anyInt())).thenReturn(true);
-        reset(getServices().userManagerInternal);
         dpm.clearDeviceOwnerApp(admin1.getPackageName());
 
         // Now DO shouldn't be set.
@@ -1181,9 +1179,8 @@
                 MockUtils.checkUserHandle(UserHandle.USER_SYSTEM));
 
         verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions(
-                eq(UserHandle.USER_SYSTEM),
-                MockUtils.checkUserRestrictions(),
-                eq(UserManagerInternal.OWNER_TYPE_DEVICE_OWNER));
+                eq(UserHandle.USER_SYSTEM), MockUtils.checkUserRestrictions(),
+                MockUtils.checkUserRestrictions(UserHandle.USER_SYSTEM), eq(true));
 
         verify(getServices().usageStatsManagerInternal).setActiveAdminApps(
                 null, UserHandle.USER_SYSTEM);
@@ -1745,15 +1742,16 @@
         verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions(
                 eq(UserHandle.USER_SYSTEM),
                 MockUtils.checkUserRestrictions(UserManager.DISALLOW_ADD_USER),
-                eq(UserManagerInternal.OWNER_TYPE_DEVICE_OWNER));
+                MockUtils.checkUserRestrictions(UserHandle.USER_SYSTEM), eq(true));
         reset(getServices().userManagerInternal);
 
         dpm.addUserRestriction(admin1, UserManager.DISALLOW_OUTGOING_CALLS);
         verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions(
                 eq(UserHandle.USER_SYSTEM),
-                MockUtils.checkUserRestrictions(UserManager.DISALLOW_OUTGOING_CALLS,
-                        UserManager.DISALLOW_ADD_USER),
-                eq(UserManagerInternal.OWNER_TYPE_DEVICE_OWNER));
+                MockUtils.checkUserRestrictions(UserManager.DISALLOW_ADD_USER),
+                MockUtils.checkUserRestrictions(UserHandle.USER_SYSTEM,
+                        UserManager.DISALLOW_OUTGOING_CALLS),
+                eq(true));
         reset(getServices().userManagerInternal);
 
         DpmTestUtils.assertRestrictions(
@@ -1770,8 +1768,10 @@
         dpm.clearUserRestriction(admin1, UserManager.DISALLOW_ADD_USER);
         verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions(
                 eq(UserHandle.USER_SYSTEM),
-                MockUtils.checkUserRestrictions(UserManager.DISALLOW_OUTGOING_CALLS),
-                eq(UserManagerInternal.OWNER_TYPE_DEVICE_OWNER));
+                MockUtils.checkUserRestrictions(),
+                MockUtils.checkUserRestrictions(UserHandle.USER_SYSTEM,
+                        UserManager.DISALLOW_OUTGOING_CALLS),
+                eq(true));
         reset(getServices().userManagerInternal);
 
         DpmTestUtils.assertRestrictions(
@@ -1787,7 +1787,7 @@
         verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions(
                 eq(UserHandle.USER_SYSTEM),
                 MockUtils.checkUserRestrictions(),
-                eq(UserManagerInternal.OWNER_TYPE_DEVICE_OWNER));
+                MockUtils.checkUserRestrictions(UserHandle.USER_SYSTEM), eq(true));
         reset(getServices().userManagerInternal);
 
         assertNoDeviceOwnerRestrictions();
@@ -1801,7 +1801,7 @@
                 eq(UserHandle.USER_SYSTEM),
                 MockUtils.checkUserRestrictions(UserManager.DISALLOW_ADJUST_VOLUME,
                         UserManager.DISALLOW_UNMUTE_MICROPHONE),
-                eq(UserManagerInternal.OWNER_TYPE_DEVICE_OWNER));
+                MockUtils.checkUserRestrictions(UserHandle.USER_SYSTEM), eq(true));
         reset(getServices().userManagerInternal);
 
         dpm.clearUserRestriction(admin1, UserManager.DISALLOW_ADJUST_VOLUME);
@@ -1813,7 +1813,7 @@
         verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions(
                 eq(UserHandle.USER_SYSTEM),
                 MockUtils.checkUserRestrictions(UserManager.DISALLOW_ADD_USER),
-                eq(UserManagerInternal.OWNER_TYPE_DEVICE_OWNER));
+                MockUtils.checkUserRestrictions(UserHandle.USER_SYSTEM), eq(true));
         reset(getServices().userManagerInternal);
 
         dpm.addUserRestriction(admin1, UserManager.DISALLOW_FUN);
@@ -1821,7 +1821,7 @@
                 eq(UserHandle.USER_SYSTEM),
                 MockUtils.checkUserRestrictions(UserManager.DISALLOW_FUN,
                         UserManager.DISALLOW_ADD_USER),
-                eq(UserManagerInternal.OWNER_TYPE_DEVICE_OWNER));
+                MockUtils.checkUserRestrictions(UserHandle.USER_SYSTEM), eq(true));
         reset(getServices().userManagerInternal);
 
         dpm.setCameraDisabled(admin1, true);
@@ -1830,7 +1830,7 @@
                 // DISALLOW_CAMERA will be applied globally.
                 MockUtils.checkUserRestrictions(UserManager.DISALLOW_FUN,
                         UserManager.DISALLOW_ADD_USER, UserManager.DISALLOW_CAMERA),
-                eq(UserManagerInternal.OWNER_TYPE_DEVICE_OWNER));
+                MockUtils.checkUserRestrictions(UserHandle.USER_SYSTEM), eq(true));
         reset(getServices().userManagerInternal);
     }
 
@@ -1887,17 +1887,19 @@
         dpm.addUserRestriction(admin1, UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);
         verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions(
                 eq(DpmMockContext.CALLER_USER_HANDLE),
-                MockUtils.checkUserRestrictions(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES),
-                eq(UserManagerInternal.OWNER_TYPE_PROFILE_OWNER));
-        reset(getServices().userManagerInternal);
+                MockUtils.checkUserRestrictions(),
+                MockUtils.checkUserRestrictions(DpmMockContext.CALLER_USER_HANDLE,
+                        UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES),
+                eq(false));
 
         dpm.addUserRestriction(admin1, UserManager.DISALLOW_OUTGOING_CALLS);
         verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions(
                 eq(DpmMockContext.CALLER_USER_HANDLE),
-                MockUtils.checkUserRestrictions(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
+                MockUtils.checkUserRestrictions(),
+                MockUtils.checkUserRestrictions(DpmMockContext.CALLER_USER_HANDLE,
+                        UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
                         UserManager.DISALLOW_OUTGOING_CALLS),
-                eq(UserManagerInternal.OWNER_TYPE_PROFILE_OWNER));
-        reset(getServices().userManagerInternal);
+                eq(false));
 
         DpmTestUtils.assertRestrictions(
                 DpmTestUtils.newRestrictions(
@@ -1918,9 +1920,10 @@
         dpm.clearUserRestriction(admin1, UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);
         verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions(
                 eq(DpmMockContext.CALLER_USER_HANDLE),
-                MockUtils.checkUserRestrictions(UserManager.DISALLOW_OUTGOING_CALLS),
-                eq(UserManagerInternal.OWNER_TYPE_PROFILE_OWNER));
-        reset(getServices().userManagerInternal);
+                MockUtils.checkUserRestrictions(),
+                MockUtils.checkUserRestrictions(DpmMockContext.CALLER_USER_HANDLE,
+                        UserManager.DISALLOW_OUTGOING_CALLS),
+                eq(false));
 
         DpmTestUtils.assertRestrictions(
                 DpmTestUtils.newRestrictions(
@@ -1940,8 +1943,7 @@
         verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions(
                 eq(DpmMockContext.CALLER_USER_HANDLE),
                 MockUtils.checkUserRestrictions(),
-                eq(UserManagerInternal.OWNER_TYPE_PROFILE_OWNER));
-        reset(getServices().userManagerInternal);
+                MockUtils.checkUserRestrictions(DpmMockContext.CALLER_USER_HANDLE), eq(false));
 
         DpmTestUtils.assertRestrictions(
                 DpmTestUtils.newRestrictions(),
@@ -1956,21 +1958,25 @@
         // DISALLOW_ADJUST_VOLUME and DISALLOW_UNMUTE_MICROPHONE can be set by PO too, even
         // though when DO sets them they'll be applied globally.
         dpm.addUserRestriction(admin1, UserManager.DISALLOW_ADJUST_VOLUME);
-        reset(getServices().userManagerInternal);
+
         dpm.addUserRestriction(admin1, UserManager.DISALLOW_UNMUTE_MICROPHONE);
         verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions(
                 eq(DpmMockContext.CALLER_USER_HANDLE),
-                MockUtils.checkUserRestrictions(UserManager.DISALLOW_ADJUST_VOLUME,
+                MockUtils.checkUserRestrictions(),
+                MockUtils.checkUserRestrictions(DpmMockContext.CALLER_USER_HANDLE,
+                        UserManager.DISALLOW_ADJUST_VOLUME,
                         UserManager.DISALLOW_UNMUTE_MICROPHONE),
-                eq(UserManagerInternal.OWNER_TYPE_PROFILE_OWNER));
-        reset(getServices().userManagerInternal);
+                eq(false));
 
         dpm.setCameraDisabled(admin1, true);
         verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions(
                 eq(DpmMockContext.CALLER_USER_HANDLE),
-                MockUtils.checkUserRestrictions(UserManager.DISALLOW_ADJUST_VOLUME,
-                        UserManager.DISALLOW_UNMUTE_MICROPHONE, UserManager.DISALLOW_CAMERA),
-                eq(UserManagerInternal.OWNER_TYPE_PROFILE_OWNER));
+                MockUtils.checkUserRestrictions(),
+                MockUtils.checkUserRestrictions(DpmMockContext.CALLER_USER_HANDLE,
+                        UserManager.DISALLOW_ADJUST_VOLUME,
+                        UserManager.DISALLOW_UNMUTE_MICROPHONE,
+                        UserManager.DISALLOW_CAMERA),
+                eq(false));
         reset(getServices().userManagerInternal);
 
         // TODO Make sure restrictions are written to the file.
@@ -2004,15 +2010,14 @@
             );
 
     public void testSetUserRestriction_asPoOfOrgOwnedDevice() throws Exception {
-        final int MANAGED_PROFILE_USER_ID = DpmMockContext.CALLER_USER_HANDLE;
         final int MANAGED_PROFILE_ADMIN_UID =
-                UserHandle.getUid(MANAGED_PROFILE_USER_ID, DpmMockContext.SYSTEM_UID);
+                UserHandle.getUid(DpmMockContext.CALLER_USER_HANDLE, DpmMockContext.SYSTEM_UID);
         mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID;
 
         addManagedProfile(admin1, MANAGED_PROFILE_ADMIN_UID, admin1);
         configureProfileOwnerOfOrgOwnedDevice(admin1, DpmMockContext.CALLER_USER_HANDLE);
 
-        when(getServices().userManager.getProfileParent(MANAGED_PROFILE_USER_ID))
+        when(getServices().userManager.getProfileParent(DpmMockContext.CALLER_USER_HANDLE))
                 .thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0));
 
         for (String restriction : PROFILE_OWNER_ORGANIZATION_OWNED_GLOBAL_RESTRICTIONS) {
@@ -2021,16 +2026,20 @@
 
         parentDpm.setCameraDisabled(admin1, true);
         verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions(
-                eq(MANAGED_PROFILE_USER_ID),
+                eq(DpmMockContext.CALLER_USER_HANDLE),
                 MockUtils.checkUserRestrictions(UserManager.DISALLOW_CAMERA),
-                eq(UserManagerInternal.OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE));
-        reset(getServices().userManagerInternal);
+                MockUtils.checkUserRestrictions(DpmMockContext.CALLER_USER_HANDLE),
+                eq(false));
+        DpmTestUtils.assertRestrictions(
+                DpmTestUtils.newRestrictions(UserManager.DISALLOW_CAMERA),
+                parentDpm.getUserRestrictions(admin1)
+        );
 
         parentDpm.setCameraDisabled(admin1, false);
-        verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions(
-                eq(MANAGED_PROFILE_USER_ID),
-                MockUtils.checkUserRestrictions(),
-                eq(UserManagerInternal.OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE));
+        DpmTestUtils.assertRestrictions(
+                DpmTestUtils.newRestrictions(),
+                parentDpm.getUserRestrictions(admin1)
+        );
         reset(getServices().userManagerInternal);
     }
 
@@ -2039,13 +2048,13 @@
         verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions(
                 eq(DpmMockContext.CALLER_USER_HANDLE),
                 MockUtils.checkUserRestrictions(restriction),
-                eq(UserManagerInternal.OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE));
+                MockUtils.checkUserRestrictions(DpmMockContext.CALLER_USER_HANDLE),
+                eq(false));
         parentDpm.clearUserRestriction(admin1, restriction);
         DpmTestUtils.assertRestrictions(
                 DpmTestUtils.newRestrictions(),
                 parentDpm.getUserRestrictions(admin1)
         );
-        reset(getServices().userManagerInternal);
     }
 
     public void testNoDefaultEnabledUserRestrictions() throws Exception {
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockUtils.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockUtils.java
index 09a6819..15f3ed1 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockUtils.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockUtils.java
@@ -15,10 +15,6 @@
  */
 package com.android.server.devicepolicy;
 
-import com.google.common.base.Objects;
-
-import com.android.server.pm.UserRestrictionsUtils;
-
 import android.content.ComponentName;
 import android.content.Intent;
 import android.os.BaseBundle;
@@ -26,6 +22,11 @@
 import android.os.UserHandle;
 import android.util.ArraySet;
 
+import com.android.server.pm.RestrictionsSet;
+import com.android.server.pm.UserRestrictionsUtils;
+
+import com.google.common.base.Objects;
+
 import org.hamcrest.BaseMatcher;
 import org.hamcrest.Description;
 import org.hamcrest.Matcher;
@@ -106,11 +107,14 @@
     }
 
     public static Bundle checkUserRestrictions(String... keys) {
-        final Bundle expected = DpmTestUtils.newRestrictions(java.util.Objects.requireNonNull(keys));
+        final Bundle expected = DpmTestUtils.newRestrictions(
+                java.util.Objects.requireNonNull(keys));
         final Matcher<Bundle> m = new BaseMatcher<Bundle>() {
             @Override
             public boolean matches(Object item) {
-                if (item == null) return false;
+                if (item == null) {
+                    return false;
+                }
                 return UserRestrictionsUtils.areEqual((Bundle) item, expected);
             }
 
@@ -122,6 +126,26 @@
         return MockitoHamcrest.argThat(m);
     }
 
+    public static RestrictionsSet checkUserRestrictions(int userId, String... keys) {
+        final RestrictionsSet expected = DpmTestUtils.newRestrictions(userId,
+                java.util.Objects.requireNonNull(keys));
+        final Matcher<RestrictionsSet> m = new BaseMatcher<RestrictionsSet>() {
+            @Override
+            public boolean matches(Object item) {
+                if (item == null) return false;
+                RestrictionsSet actual = (RestrictionsSet) item;
+                return UserRestrictionsUtils.areEqual(expected.getRestrictions(userId),
+                        actual.getRestrictions(userId));
+            }
+
+            @Override
+            public void describeTo(Description description) {
+                description.appendText("User restrictions=" + getRestrictionsAsString(expected));
+            }
+        };
+        return MockitoHamcrest.argThat(m);
+    }
+
     public static Set<String> checkApps(String... adminApps) {
         final Matcher<Set<String>> m = new BaseMatcher<Set<String>>() {
             @Override
@@ -146,6 +170,23 @@
         return MockitoHamcrest.argThat(m);
     }
 
+    private static String getRestrictionsAsString(RestrictionsSet r) {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("{");
+
+        if (r != null) {
+            String sep = "";
+            for (int i = 0; i < r.size(); i++) {
+                sb.append(sep);
+                sep = ",";
+                sb.append(
+                        String.format("%s= %s", r.keyAt(i), getRestrictionsAsString(r.valueAt(i))));
+            }
+        }
+        sb.append("}");
+        return sb.toString();
+    }
+
     private static String getRestrictionsAsString(Bundle b) {
         final StringBuilder sb = new StringBuilder();
         sb.append("[");
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
index 1c2313e..dc181a9 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
@@ -22,7 +22,6 @@
 import android.os.Bundle;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.os.UserManagerInternal;
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.util.SparseArray;
@@ -118,135 +117,6 @@
                 UserManager.DISALLOW_ADJUST_VOLUME, user));
     }
 
-    public void testSortToGlobalAndLocal() {
-        final Bundle local = new Bundle();
-        final Bundle global = new Bundle();
-
-        UserRestrictionsUtils.sortToGlobalAndLocal(null,
-                UserManagerInternal.OWNER_TYPE_PROFILE_OWNER,
-                global, local);
-        assertEquals(0, global.size());
-        assertEquals(0, local.size());
-
-        UserRestrictionsUtils.sortToGlobalAndLocal(Bundle.EMPTY,
-                UserManagerInternal.OWNER_TYPE_PROFILE_OWNER,
-                global, local);
-        assertEquals(0, global.size());
-        assertEquals(0, local.size());
-
-        // Restrictions set by DO.
-        UserRestrictionsUtils.sortToGlobalAndLocal(newRestrictions(
-                UserManager.DISALLOW_ADJUST_VOLUME,
-                UserManager.DISALLOW_UNMUTE_MICROPHONE,
-                UserManager.DISALLOW_USB_FILE_TRANSFER,
-                UserManager.DISALLOW_CONFIG_TETHERING,
-                UserManager.DISALLOW_OUTGOING_BEAM,
-                UserManager.DISALLOW_APPS_CONTROL,
-                UserManager.ENSURE_VERIFY_APPS,
-                UserManager.DISALLOW_CAMERA
-                ), UserManagerInternal.OWNER_TYPE_DEVICE_OWNER,
-                global, local);
-
-
-        assertRestrictions(newRestrictions(
-                // This one is global no matter who sets it.
-                UserManager.ENSURE_VERIFY_APPS,
-
-                // These can be set by PO too, but when DO sets them, they're global.
-                UserManager.DISALLOW_ADJUST_VOLUME,
-                UserManager.DISALLOW_UNMUTE_MICROPHONE,
-
-                // These can only be set by DO.
-                UserManager.DISALLOW_USB_FILE_TRANSFER,
-                UserManager.DISALLOW_CONFIG_TETHERING,
-
-                // This can be set by DO or PO of organisation owned device
-                UserManager.DISALLOW_CAMERA
-        ), global);
-
-        assertRestrictions(newRestrictions(
-                // They can be set by both DO/PO.
-                UserManager.DISALLOW_OUTGOING_BEAM,
-                UserManager.DISALLOW_APPS_CONTROL
-        ), local);
-
-        local.clear();
-        global.clear();
-
-        // Restrictions set by PO.
-        UserRestrictionsUtils.sortToGlobalAndLocal(newRestrictions(
-                UserManager.DISALLOW_ADJUST_VOLUME,
-                UserManager.DISALLOW_UNMUTE_MICROPHONE,
-                UserManager.DISALLOW_USB_FILE_TRANSFER,
-                UserManager.DISALLOW_CONFIG_TETHERING,
-                UserManager.DISALLOW_OUTGOING_BEAM,
-                UserManager.DISALLOW_APPS_CONTROL,
-                UserManager.ENSURE_VERIFY_APPS,
-                UserManager.DISALLOW_CAMERA
-                ), UserManagerInternal.OWNER_TYPE_PROFILE_OWNER,
-                global, local);
-
-        assertRestrictions(newRestrictions(
-                // This one is global no matter who sets it.
-                UserManager.ENSURE_VERIFY_APPS
-        ), global);
-
-        assertRestrictions(newRestrictions(
-                // These can be set by PO too, but when PO sets them, they're local.
-                UserManager.DISALLOW_ADJUST_VOLUME,
-                UserManager.DISALLOW_UNMUTE_MICROPHONE,
-
-                // They can be set by both DO/PO.
-                UserManager.DISALLOW_OUTGOING_BEAM,
-                UserManager.DISALLOW_APPS_CONTROL,
-
-                // These can only be set by DO.
-                UserManager.DISALLOW_USB_FILE_TRANSFER,
-                UserManager.DISALLOW_CONFIG_TETHERING,
-
-                // This can be set by DO or PO of organisation owned device
-                UserManager.DISALLOW_CAMERA
-        ), local);
-
-        local.clear();
-        global.clear();
-
-        // Restrictions set by PO of organisation owned device
-        UserRestrictionsUtils.sortToGlobalAndLocal(newRestrictions(
-                UserManager.DISALLOW_CONFIG_DATE_TIME
-                ), UserManagerInternal.OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE,
-                global, local);
-
-        assertRestrictions(newRestrictions(
-                // This user restriction is global when set by PO of org owned device
-                UserManager.DISALLOW_CONFIG_DATE_TIME
-        ), global);
-        assertEquals(0, local.size());
-    }
-
-    public void testSortToLocalAndGlobalWithCameraDisabled() {
-        final Bundle local = new Bundle();
-        final Bundle global = new Bundle();
-
-        UserRestrictionsUtils.sortToGlobalAndLocal(newRestrictions(UserManager.DISALLOW_CAMERA),
-                UserManagerInternal.OWNER_TYPE_DEVICE_OWNER, global, local);
-        assertRestrictions(newRestrictions(UserManager.DISALLOW_CAMERA), global);
-        assertEquals(0, local.size());
-        global.clear();
-
-        UserRestrictionsUtils.sortToGlobalAndLocal(newRestrictions(UserManager.DISALLOW_CAMERA),
-                UserManagerInternal.OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE, global,
-                local);
-        assertRestrictions(newRestrictions(UserManager.DISALLOW_CAMERA), global);
-        assertEquals(0, local.size());
-        global.clear();
-
-        UserRestrictionsUtils.sortToGlobalAndLocal(newRestrictions(UserManager.DISALLOW_CAMERA),
-                UserManagerInternal.OWNER_TYPE_PROFILE_OWNER, global, local);
-        assertEquals(0, global.size());
-        assertRestrictions(newRestrictions(UserManager.DISALLOW_CAMERA), local);
-    }
-
     public void testMoveRestriction() {
         SparseArray<RestrictionsSet> localRestrictions = new SparseArray<>();
         RestrictionsSet globalRestrictions = new RestrictionsSet();
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 1cca207..bdba4b6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -495,9 +495,12 @@
     }
 
     private void assertNoTasks(DisplayContent display) {
-        for (int i = display.getStackCount() - 1; i >= 0; --i) {
-            final ActivityStack stack = display.getStackAt(i);
-            assertFalse(stack.hasChild());
+        for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
+            final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
+            for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
+                final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
+                assertFalse(stack.hasChild());
+            }
         }
     }
 
@@ -1042,10 +1045,14 @@
                     // move everything to secondary because test expects this but usually sysui
                     // does it.
                     DisplayContent dc = mService.mRootWindowContainer.getDisplayContent(mDisplayId);
-                    for (int i = dc.getStackCount() - 1; i >= 0; --i) {
-                        if (!WindowConfiguration.isSplitScreenWindowingMode(
-                                dc.getStackAt(i).getWindowingMode())) {
-                            dc.getStackAt(i).reparent(mSecondary, POSITION_BOTTOM);
+                    for (int tdaNdx = dc.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
+                        final TaskDisplayArea taskDisplayArea = dc.getTaskDisplayAreaAt(tdaNdx);
+                        for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
+                            final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
+                            if (!WindowConfiguration.isSplitScreenWindowingMode(
+                                    stack.getWindowingMode())) {
+                                stack.reparent(mSecondary, POSITION_BOTTOM);
+                            }
                         }
                     }
                 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index d9c3ace..add4e9c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -686,8 +686,8 @@
         mRecentTasks.setOnlyTestVisibleRange();
         mRecentTasks.setParameters(-1 /* min */, 3 /* max */, -1 /* ms */);
 
-        final DisplayContent display = mRootWindowContainer.getDefaultDisplay();
-        final Task alwaysOnTopTask = display.createStack(WINDOWING_MODE_MULTI_WINDOW,
+        final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
+        final Task alwaysOnTopTask = taskDisplayArea.createStack(WINDOWING_MODE_MULTI_WINDOW,
                 ACTIVITY_TYPE_STANDARD, true /* onTop */);
         alwaysOnTopTask.setAlwaysOnTop(true);
 
@@ -862,8 +862,8 @@
 
         final ActivityStack homeStack = mTaskContainer.getRootHomeTask();
         final DisplayContent otherDisplay = addNewDisplayContentAt(DisplayContent.POSITION_TOP);
-        final ActivityStack otherDisplayStack = otherDisplay.createStack(
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final ActivityStack otherDisplayStack = otherDisplay.getDefaultTaskDisplayArea()
+                .createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
 
         // Add a number of tasks (beyond the max) on each display, ensure that the tasks are not
         // removed
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
index 48d4e70..b648346 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -349,8 +349,8 @@
         // Create Recents on secondary display.
         final TestDisplayContent secondDisplay = addNewDisplayContentAt(
                 DisplayContent.POSITION_TOP);
-        final ActivityStack stack = secondDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_RECENTS, true /* onTop */);
+        final ActivityStack stack = secondDisplay.getDefaultTaskDisplayArea()
+                .createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_RECENTS, true /* onTop */);
         final Task task = new TaskBuilder(mSupervisor).setStack(stack).build();
         new ActivityBuilder(mService).setTask(task).build();
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
index 2dbfd06..62d311a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
@@ -627,9 +627,12 @@
 
     private List<Task> getTasksCreatedByOrganizer(DisplayContent dc) {
         ArrayList<Task> out = new ArrayList<>();
-        for (int i = dc.getStackCount() - 1; i >= 0; --i) {
-            final Task t = dc.getStackAt(i);
-            if (t.mCreatedByOrganizer) out.add(t);
+        for (int tdaNdx = dc.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
+            final TaskDisplayArea taskDisplayArea = dc.getTaskDisplayAreaAt(tdaNdx);
+            for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
+                final Task t = taskDisplayArea.getStackAt(sNdx);
+                if (t.mCreatedByOrganizer) out.add(t);
+            }
         }
         return out;
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
index 50584c6..519ac78 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -354,7 +354,7 @@
         spyOn(parentWindowContainer);
         parentWindowContainer.setBounds(fullScreenBounds);
         doReturn(parentWindowContainer).when(task).getParent();
-        doReturn(display.mTaskContainers).when(task).getDisplayArea();
+        doReturn(display.getDefaultTaskDisplayArea()).when(task).getDisplayArea();
         doReturn(stack).when(task).getStack();
         doReturn(true).when(parentWindowContainer).handlesOrientationChangeFromDescendant();
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 820d381..71b35b6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -40,6 +40,7 @@
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doThrow;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
@@ -47,6 +48,8 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.not;
 import static org.junit.Assert.assertEquals;
@@ -55,6 +58,7 @@
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyString;
@@ -65,6 +69,7 @@
 import android.graphics.Matrix;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
+import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
 import android.util.Size;
 import android.view.DisplayCutout;
@@ -568,6 +573,36 @@
     }
 
     @Test
+    public void testReportResizedWithRemoteException() {
+        final WindowState win = mChildAppWindowAbove;
+        makeWindowVisible(win, win.getParentWindow());
+        win.mLayoutSeq = win.getDisplayContent().mLayoutSeq;
+        win.updateResizingWindowIfNeeded();
+
+        assertThat(mWm.mResizingWindows).contains(win);
+        assertTrue(win.getOrientationChanging());
+
+        mWm.mResizingWindows.remove(win);
+        spyOn(win.mClient);
+        try {
+            doThrow(new RemoteException("test")).when(win.mClient).resized(any() /* frame */,
+                    any() /* contentInsets */, any() /* visibleInsets */, any() /* stableInsets */,
+                    anyBoolean() /* reportDraw */, any() /* mergedConfig */,
+                    any() /* backDropFrame */, anyBoolean() /* forceLayout */,
+                    anyBoolean() /* alwaysConsumeSystemBars */, anyInt() /* displayId */,
+                    any() /* displayCutout */);
+        } catch (RemoteException ignored) {
+        }
+        win.reportResized();
+        win.updateResizingWindowIfNeeded();
+
+        // Even "resized" throws remote exception, it is still considered as reported. So the window
+        // shouldn't be resized again (which may block unfreeze in real case).
+        assertThat(mWm.mResizingWindows).doesNotContain(win);
+        assertFalse(win.getOrientationChanging());
+    }
+
+    @Test
     public void testGetTransformationMatrix() {
         final int PARENT_WINDOW_OFFSET = 1;
         final int DISPLAY_IN_PARENT_WINDOW_OFFSET = 2;
diff --git a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
index 316a83a..6e9dc8e 100644
--- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
@@ -55,6 +55,10 @@
 import androidx.core.os.BuildCompat;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
+
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -67,6 +71,9 @@
     private static final String TEST_SSID = "TEST_SSID";
     private static final String DIFFERENT_TEST_SSID = "DIFFERENT_TEST_SSID";
 
+    @Rule
+    public DevSdkIgnoreRule mDevSdkIgnoreRule = new DevSdkIgnoreRule();
+
     private boolean isAtLeastR() {
         // BuildCompat.isAtLeastR() is used to check the Android version before releasing Android R.
         // Build.VERSION.SDK_INT > Build.VERSION_CODES.Q is used to check the Android version after
@@ -441,7 +448,7 @@
         return range;
     }
 
-    @Test
+    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
     public void testSetAdministratorUids() {
         NetworkCapabilities nc =
                 new NetworkCapabilities().setAdministratorUids(new int[] {2, 1, 3});
@@ -449,7 +456,7 @@
         assertArrayEquals(new int[] {1, 2, 3}, nc.getAdministratorUids());
     }
 
-    @Test
+    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
     public void testSetAdministratorUidsWithDuplicates() {
         try {
             new NetworkCapabilities().setAdministratorUids(new int[] {1, 1});
@@ -510,6 +517,12 @@
         assertFalse(nc2.appliesToUid(12));
         assertTrue(nc1.appliesToUid(22));
         assertTrue(nc2.appliesToUid(22));
+    }
+
+    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+    public void testCombineCapabilities_AdministratorUids() {
+        final NetworkCapabilities nc1 = new NetworkCapabilities();
+        final NetworkCapabilities nc2 = new NetworkCapabilities();
 
         final int[] adminUids = {3, 6, 12};
         nc1.setAdministratorUids(adminUids);
@@ -518,7 +531,7 @@
         assertArrayEquals(nc2.getAdministratorUids(), adminUids);
 
         final int[] adminUidsOtherOrder = {3, 12, 6};
-        nc1.setAdministratorUids(adminUids);
+        nc1.setAdministratorUids(adminUidsOtherOrder);
         assertTrue(nc2.equalsAdministratorUids(nc1));
 
         final int[] adminUids2 = {11, 1, 12, 3, 6};
diff --git a/tests/net/common/java/android/net/netstats/NetworkStatsApiTest.kt b/tests/net/common/java/android/net/netstats/NetworkStatsApiTest.kt
new file mode 100644
index 0000000..9119d62
--- /dev/null
+++ b/tests/net/common/java/android/net/netstats/NetworkStatsApiTest.kt
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2020 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.net.netstats
+
+import android.net.NetworkStats
+import android.net.NetworkStats.DEFAULT_NETWORK_NO
+import android.net.NetworkStats.DEFAULT_NETWORK_YES
+import android.net.NetworkStats.Entry
+import android.net.NetworkStats.IFACE_VT
+import android.net.NetworkStats.METERED_NO
+import android.net.NetworkStats.METERED_YES
+import android.net.NetworkStats.ROAMING_NO
+import android.net.NetworkStats.ROAMING_YES
+import android.net.NetworkStats.SET_DEFAULT
+import android.net.NetworkStats.SET_FOREGROUND
+import android.net.NetworkStats.TAG_NONE
+import android.os.Build
+import androidx.test.filters.SmallTest
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
+import com.android.testutils.assertFieldCountEquals
+import com.android.testutils.assertNetworkStatsEquals
+import com.android.testutils.assertParcelingIsLossless
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import kotlin.test.assertEquals
+
+@RunWith(JUnit4::class)
+@SmallTest
+class NetworkStatsApiTest {
+    @Rule
+    @JvmField
+    val ignoreRule = DevSdkIgnoreRule()
+
+    private val testStatsEmpty = NetworkStats(0L, 0)
+
+    // stats1 and stats2 will have some entries with common keys, which are expected to
+    // be merged if performing add on these 2 stats.
+    private val testStats1 = NetworkStats(0L, 0)
+            // Entries which only appear in set1.
+            .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
+                    METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, 20, 3, 57, 40, 3))
+            .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
+                    METERED_NO, ROAMING_YES, DEFAULT_NETWORK_NO, 31, 7, 24, 5, 8))
+            .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
+                    METERED_YES, ROAMING_NO, DEFAULT_NETWORK_NO, 25, 3, 47, 8, 2))
+            .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_FOREGROUND, TAG_NONE,
+                    METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 37, 52, 1, 10, 4))
+            // Entries which are common for set1 and set2.
+            .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
+                    METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 101, 2, 103, 4, 5))
+            .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, 0x80,
+                    METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 17, 2, 11, 1, 0))
+            .addEntry(Entry(TEST_IFACE, TEST_UID2, SET_DEFAULT, TAG_NONE,
+                    METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 40, 1, 0, 0, 8))
+            .addEntry(Entry(IFACE_VT, TEST_UID1, SET_DEFAULT, TAG_NONE,
+                    METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 3, 1, 6, 2, 0))
+
+    private val testStats2 = NetworkStats(0L, 0)
+            // Entries which are common for set1 and set2.
+            .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, 0x80,
+                    METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 3, 15, 2, 31, 1))
+            .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_FOREGROUND, TAG_NONE,
+                    METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 13, 61, 10, 1, 45))
+            .addEntry(Entry(TEST_IFACE, TEST_UID2, SET_DEFAULT, TAG_NONE,
+                    METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 11, 2, 3, 4, 7))
+            .addEntry(Entry(IFACE_VT, TEST_UID1, SET_DEFAULT, TAG_NONE,
+                    METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 4, 3, 2, 1, 0))
+            // Entry which only appears in set2.
+            .addEntry(Entry(IFACE_VT, TEST_UID2, SET_DEFAULT, TAG_NONE,
+                    METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 2, 3, 7, 8, 0))
+
+    // This is a result of adding stats1 and stats2, while the merging of common key items is
+    // subject to test later, this should not be initialized with for a loop to add stats1
+    // and stats2 above.
+    private val testStats3 = NetworkStats(0L, 9)
+            // Entries which are unique either in stats1 or stats2.
+            .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
+                    METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 101, 2, 103, 4, 5))
+            .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
+                    METERED_NO, ROAMING_YES, DEFAULT_NETWORK_NO, 31, 7, 24, 5, 8))
+            .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
+                    METERED_YES, ROAMING_NO, DEFAULT_NETWORK_NO, 25, 3, 47, 8, 2))
+            .addEntry(Entry(IFACE_VT, TEST_UID2, SET_DEFAULT, TAG_NONE,
+                    METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 2, 3, 7, 8, 0))
+            // Entries which are common for stats1 and stats2 are being merged.
+            .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
+                    METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, 20, 3, 57, 40, 3))
+            .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, 0x80,
+                    METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 20, 17, 13, 32, 1))
+            .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_FOREGROUND, TAG_NONE,
+                    METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 50, 113, 11, 11, 49))
+            .addEntry(Entry(TEST_IFACE, TEST_UID2, SET_DEFAULT, TAG_NONE,
+                    METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 51, 3, 3, 4, 15))
+            .addEntry(Entry(IFACE_VT, TEST_UID1, SET_DEFAULT, TAG_NONE,
+                    METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 7, 4, 8, 3, 0))
+
+    companion object {
+        private const val TEST_IFACE = "test0"
+        private const val TEST_UID1 = 1001
+        private const val TEST_UID2 = 1002
+    }
+
+    @Before
+    fun setUp() {
+        assertEquals(8, testStats1.size())
+        assertEquals(5, testStats2.size())
+        assertEquals(9, testStats3.size())
+    }
+
+    @Test
+    @IgnoreUpTo(Build.VERSION_CODES.Q)
+    fun testAddEntry() {
+        val expectedEntriesInStats2 = arrayOf(
+                Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, 0x80,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 3, 15, 2, 31, 1),
+                Entry(TEST_IFACE, TEST_UID1, SET_FOREGROUND, TAG_NONE,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 13, 61, 10, 1, 45),
+                Entry(TEST_IFACE, TEST_UID2, SET_DEFAULT, TAG_NONE,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 11, 2, 3, 4, 7),
+                Entry(IFACE_VT, TEST_UID1, SET_DEFAULT, TAG_NONE,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 4, 3, 2, 1, 0),
+                Entry(IFACE_VT, TEST_UID2, SET_DEFAULT, TAG_NONE,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 2, 3, 7, 8, 0))
+
+        // While testStats* are already initialized with addEntry, verify content added
+        // matches expectation.
+        for (i in expectedEntriesInStats2.indices) {
+            val entry = testStats2.getValues(i, null)
+            assertEquals(expectedEntriesInStats2[i], entry)
+        }
+
+        // Verify entry updated with addEntry.
+        val stats = testStats2.addEntry(Entry(IFACE_VT, TEST_UID1, SET_DEFAULT, TAG_NONE,
+                METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 12, -5, 7, 0, 9))
+        assertEquals(Entry(IFACE_VT, TEST_UID1, SET_DEFAULT, TAG_NONE,
+                METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 16, -2, 9, 1, 9),
+                stats.getValues(3, null))
+    }
+
+    @Test
+    @IgnoreUpTo(Build.VERSION_CODES.Q)
+    fun testAdd() {
+        var stats = NetworkStats(0L, 0)
+        assertNetworkStatsEquals(testStatsEmpty, stats)
+        stats = stats.add(testStats2)
+        assertNetworkStatsEquals(testStats2, stats)
+        stats = stats.add(testStats1)
+        // EMPTY + STATS2 + STATS1 = STATS3
+        assertNetworkStatsEquals(testStats3, stats)
+    }
+
+    @Test
+    @IgnoreUpTo(Build.VERSION_CODES.Q)
+    fun testParcelUnparcel() {
+        assertParcelingIsLossless(testStatsEmpty)
+        assertParcelingIsLossless(testStats1)
+        assertParcelingIsLossless(testStats2)
+        assertFieldCountEquals(15, NetworkStats::class.java)
+    }
+
+    @Test
+    @IgnoreUpTo(Build.VERSION_CODES.Q)
+    fun testDescribeContents() {
+        assertEquals(0, testStatsEmpty.describeContents())
+        assertEquals(0, testStats1.describeContents())
+        assertEquals(0, testStats2.describeContents())
+        assertEquals(0, testStats3.describeContents())
+    }
+
+    @Test
+    @IgnoreUpTo(Build.VERSION_CODES.Q)
+    fun testSubtract() {
+        // STATS3 - STATS2 = STATS1
+        assertNetworkStatsEquals(testStats1, testStats3.subtract(testStats2))
+        // STATS3 - STATS1 = STATS2
+        assertNetworkStatsEquals(testStats2, testStats3.subtract(testStats1))
+    }
+
+    @Test
+    @IgnoreUpTo(Build.VERSION_CODES.Q)
+    fun testMethodsDontModifyReceiver() {
+        listOf(testStatsEmpty, testStats1, testStats2, testStats3).forEach {
+            val origStats = it.clone()
+            it.addEntry(Entry(TEST_IFACE, TEST_UID1, SET_FOREGROUND, TAG_NONE,
+                    METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 13, 61, 10, 1, 45))
+            it.add(testStats3)
+            it.subtract(testStats1)
+            assertNetworkStatsEquals(origStats, it)
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/net/java/android/net/NetworkStatsTest.java b/tests/net/java/android/net/NetworkStatsTest.java
index e71d599..98f705f 100644
--- a/tests/net/java/android/net/NetworkStatsTest.java
+++ b/tests/net/java/android/net/NetworkStatsTest.java
@@ -503,6 +503,53 @@
     }
 
     @Test
+    public void testRemoveEmptyEntries() throws Exception {
+        // Test empty stats.
+        final NetworkStats statsEmpty = new NetworkStats(TEST_START, 3);
+        assertEquals(0, statsEmpty.removeEmptyEntries().size());
+
+        // Test stats with non-zero entry.
+        final NetworkStats statsNonZero = new NetworkStats(TEST_START, 1)
+                .insertEntry(TEST_IFACE, 99, SET_DEFAULT, TAG_NONE, METERED_NO,
+                        ROAMING_NO, DEFAULT_NETWORK_NO, 1L, 128L, 0L, 2L, 20L);
+        assertEquals(1, statsNonZero.size());
+        final NetworkStats expectedNonZero = statsNonZero.removeEmptyEntries();
+        assertEquals(1, expectedNonZero.size());
+        assertValues(expectedNonZero, 0, TEST_IFACE, 99, SET_DEFAULT, TAG_NONE, METERED_NO,
+                ROAMING_NO, DEFAULT_NETWORK_NO, 1L, 128L, 0L, 2L, 20L);
+
+        // Test stats with empty entry.
+        final NetworkStats statsZero = new NetworkStats(TEST_START, 1)
+                .insertEntry(TEST_IFACE, 99, SET_DEFAULT, TAG_NONE, METERED_NO,
+                        ROAMING_NO, DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L);
+        assertEquals(1, statsZero.size());
+        final NetworkStats expectedZero = statsZero.removeEmptyEntries();
+        assertEquals(1, statsZero.size()); // Assert immutable.
+        assertEquals(0, expectedZero.size());
+
+        // Test stats with multiple entries.
+        final NetworkStats statsMultiple = new NetworkStats(TEST_START, 0)
+                .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 2L, 64L, 0L, 2L, 20L)
+                .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 4L, 32L, 0L, 0L, 0L)
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 0L, 0L, 0L, 0L, 0L)
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 0L, 0L, 0L, 0L, 0L)
+                .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, 8L, 0L, 0L, 0L, 0L)
+                .insertEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, 0L, 8L, 0L, 0L, 0L)
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 4L, 0L, 0L)
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 0L, 0L, 0L, 2L, 0L)
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 0L, 0L, 0L, 0L, 1L);
+        assertEquals(9, statsMultiple.size());
+        final NetworkStats expectedMultiple = statsMultiple.removeEmptyEntries();
+        assertEquals(9, statsMultiple.size()); // Assert immutable.
+        assertEquals(7, expectedMultiple.size());
+        assertValues(expectedMultiple.getTotalIncludingTags(null), 14L, 104L, 4L, 4L, 21L);
+
+        // Test stats with multiple empty entries.
+        assertEquals(statsMultiple.size(), statsMultiple.subtract(statsMultiple).size());
+        assertEquals(0, statsMultiple.subtract(statsMultiple).removeEmptyEntries().size());
+    }
+
+    @Test
     public void testClone() throws Exception {
         final NetworkStats original = new NetworkStats(TEST_START, 5)
                 .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 2L, 20L)