Introducing private tab in profile tabs and sidebar.

Updated ProfileTabs.java to user UserManagerState to get list of userIds
for which the tabs have to created, when the feature flag for private
space is enabled. RootsFragment and other associated classes have also
been updated to display private profile (or any other new profile)
section in the sidebar.

Bug: 309754864
Test: atest unit tests and manual testing
Change-Id: I130cda26346f7df81fabbb9c9f1ba92b1b859929
diff --git a/src/com/android/documentsui/AbstractActionHandler.java b/src/com/android/documentsui/AbstractActionHandler.java
index 4407a62..e348d6b 100644
--- a/src/com/android/documentsui/AbstractActionHandler.java
+++ b/src/com/android/documentsui/AbstractActionHandler.java
@@ -119,6 +119,7 @@
     public void registerDisplayStateChangedListener(Runnable l) {
         mDisplayStateChangedListener = l;
     }
+
     @Override
     public void unregisterDisplayStateChangedListener(Runnable l) {
         if (mDisplayStateChangedListener == l) {
@@ -135,12 +136,12 @@
             Lookup<String, Executor> executors,
             Injector<?> injector) {
 
-        assert(activity != null);
-        assert(state != null);
-        assert(providers != null);
-        assert(searchMgr != null);
-        assert(docs != null);
-        assert(injector != null);
+        assert (activity != null);
+        assert (state != null);
+        assert (providers != null);
+        assert (searchMgr != null);
+        assert (docs != null);
+        assert (injector != null);
 
         mActivity = activity;
         mState = state;
@@ -359,7 +360,7 @@
 
     @Override
     public void openContainerDocument(DocumentInfo doc) {
-        assert(doc.isContainer());
+        assert (doc.isContainer());
 
         if (mSearchMgr.isSearching()) {
             loadDocument(
@@ -372,6 +373,7 @@
     }
 
     // TODO: Make this private and make tests call interface method instead.
+
     /**
      * Behavior when a document is opened.
      */
@@ -620,7 +622,7 @@
             currentDoc = mDocs.getArchiveDocument(doc.derivedUri, doc.userId);
         }
 
-        assert(currentDoc != null);
+        assert (currentDoc != null);
         if (currentDoc.equals(mState.stack.peek())) {
             Log.w(TAG, "This DocumentInfo is already in current DocumentsStack");
             return;
@@ -676,8 +678,8 @@
             ComponentName component = new ComponentName(
                     mActivity.getPackageName(), Shared.LAUNCHER_TARGET_CLASS);
             pm.setComponentEnabledSetting(component, enalbled
-                    ? PackageManager.COMPONENT_ENABLED_STATE_DISABLED
-                    : PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
+                            ? PackageManager.COMPONENT_ENABLED_STATE_DISABLED
+                            : PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
                     PackageManager.DONT_KILL_APP);
         }
     }
@@ -714,7 +716,7 @@
                 mDocs,
                 userId,
                 callback
-                ).executeOnExecutor(mExecutors.lookup(uri.getAuthority()), uri);
+        ).executeOnExecutor(mExecutors.lookup(uri.getAuthority()), uri);
     }
 
     @Override
@@ -826,8 +828,8 @@
     private void onRootLoaded(@Nullable RootInfo root) {
         boolean invalidRootForAction =
                 (root != null
-                && !root.supportsChildren()
-                && mState.action == State.ACTION_OPEN_TREE);
+                        && !root.supportsChildren()
+                        && mState.action == State.ACTION_OPEN_TREE);
 
         if (invalidRootForAction) {
             loadDeviceRoot();
@@ -942,8 +944,8 @@
 
                 if (DEBUG) {
                     Log.d(TAG,
-                        "Creating new directory loader for: "
-                            + DocumentInfo.debugString(mState.stack.peek()));
+                            "Creating new directory loader for: "
+                                    + DocumentInfo.debugString(mState.stack.peek()));
                 }
 
                 return new DirectoryLoader(
@@ -961,9 +963,9 @@
         public void onLoadFinished(Loader<DirectoryResult> loader, DirectoryResult result) {
             if (DEBUG) {
                 Log.d(TAG, "Loader has finished for: "
-                    + DocumentInfo.debugString(mState.stack.peek()));
+                        + DocumentInfo.debugString(mState.stack.peek()));
             }
-            assert(result != null);
+            assert (result != null);
 
             mInjector.getModel().update(result);
             mLoaderSemaphore.release();
@@ -974,24 +976,34 @@
             mLoaderSemaphore.release();
         }
     }
+
     /**
      * A class primarily for the support of isolating our tests
      * from our concrete activity implementations.
      */
     public interface CommonAddons {
         void restoreRootAndDirectory();
+
         void refreshCurrentRootAndDirectory(@AnimationType int anim);
+
         void onRootPicked(RootInfo root);
+
         // TODO: Move this to PickAddons as multi-document picking is exclusive to that activity.
         void onDocumentsPicked(List<DocumentInfo> docs);
+
         void onDocumentPicked(DocumentInfo doc);
+
         RootInfo getCurrentRoot();
+
         DocumentInfo getCurrentDirectory();
+
         UserId getSelectedUser();
+
         /**
          * Check whether current directory is root of recent.
          */
         boolean isInRecents();
+
         void setRootsDrawerOpen(boolean open);
 
         /**
diff --git a/src/com/android/documentsui/MultiRootDocumentsLoader.java b/src/com/android/documentsui/MultiRootDocumentsLoader.java
index 7668b06..f450814 100644
--- a/src/com/android/documentsui/MultiRootDocumentsLoader.java
+++ b/src/com/android/documentsui/MultiRootDocumentsLoader.java
@@ -196,11 +196,11 @@
 
                         final FilteringCursorWrapper filteredCursor =
                                 new FilteringCursorWrapper(cursor) {
-                            @Override
-                            public void close() {
-                                // Ignored, since we manage cursor lifecycle internally
-                            }
-                        };
+                                    @Override
+                                    public void close() {
+                                        // Ignored, since we manage cursor lifecycle internally
+                                    }
+                                };
                         filteredCursor.filterHiddenFiles(mState.showHiddenFiles);
                         filteredCursor.filterMimes(mState.acceptMimes, getRejectMimes());
                         filteredCursor.filterLastModified(rejectBefore);
diff --git a/src/com/android/documentsui/ProfileTabs.java b/src/com/android/documentsui/ProfileTabs.java
index ded78e0..43c12e4 100644
--- a/src/com/android/documentsui/ProfileTabs.java
+++ b/src/com/android/documentsui/ProfileTabs.java
@@ -23,6 +23,7 @@
 
 import android.app.admin.DevicePolicyManager;
 import android.os.Build;
+import android.os.UserManager;
 import android.view.View;
 import android.view.ViewGroup;
 
@@ -38,8 +39,10 @@
 import com.google.android.material.tabs.TabLayout;
 import com.google.common.base.Objects;
 
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 
 /**
  * A manager class to control UI on a {@link TabLayout} for cross-profile purpose.
@@ -114,7 +117,7 @@
     }
 
     /**
-     * Update the tab layout based on conditions.
+     * Update the tab layout based on status of availability of the hidden profile.
      */
     public void updateView() {
         updateTabsIfNeeded();
@@ -124,8 +127,13 @@
             // Update the layout according to the current root if necessary.
             // Make sure we do not invoke callback. Otherwise, it is likely to cause infinite loop.
             mTabs.removeOnTabSelectedListener(mOnTabSelectedListener);
-            mTabs.selectTab(mTabs.getTabAt(mUserIds.indexOf(currentRoot.userId)));
-            mTabs.addOnTabSelectedListener(mOnTabSelectedListener);
+            if (!mUserIds.contains(currentRoot.userId)) {
+                mTabs.addOnTabSelectedListener(mOnTabSelectedListener);
+                mTabs.selectTab(mTabs.getTabAt(mUserIds.indexOf(UserId.CURRENT_USER)));
+            } else {
+                mTabs.selectTab(mTabs.getTabAt(mUserIds.indexOf(currentRoot.userId)));
+                mTabs.addOnTabSelectedListener(mOnTabSelectedListener);
+            }
         }
         mTabsContainer.setVisibility(shouldShow() ? View.VISIBLE : View.GONE);
 
@@ -172,16 +180,15 @@
         // Given that mUserIds was initialized with only the current user, if getUserIds()
         // returns just the current user, we don't need to do anything on the tab layout.
         if (!userIds.equals(mUserIds)) {
-            mUserIds = userIds;
+            mUserIds = new ArrayList<>();
+            mUserIds.addAll(userIds);
             mTabs.removeAllTabs();
             if (mUserIds.size() > 1) {
-                // set setSelected to false otherwise it will trigger callback.
-                mTabs.addTab(createTab(
-                        getEnterpriseString(PERSONAL_TAB, R.string.personal_tab),
-                        mUserIdManager.getSystemUser()), /* setSelected= */false);
-                mTabs.addTab(createTab(
-                        getEnterpriseString(WORK_TAB, R.string.work_tab),
-                        mUserIdManager.getManagedUser()), /* setSelected= */false);
+                if (FeatureFlagUtils.isPrivateSpaceEnabled()) {
+                    addTabsPrivateSpaceEnabled();
+                } else {
+                    addTabsPrivateSpaceDisabled();
+                }
             }
         }
     }
@@ -195,6 +202,28 @@
         return mUserIdManager.getUserIds();
     }
 
+    private void addTabsPrivateSpaceEnabled() {
+        // set setSelected to false otherwise it will trigger callback.
+        assert mUserManagerState != null;
+        Map<UserId, String> userIdToLabelMap = mUserManagerState.getUserIdToLabelMap();
+        UserManager userManager = mTabsContainer.getContext().getSystemService(UserManager.class);
+        assert userManager != null;
+        for (UserId userId : mUserIds) {
+            mTabs.addTab(createTab(userIdToLabelMap.get(userId), userId), /* setSelected= */false);
+        }
+    }
+
+    private void addTabsPrivateSpaceDisabled() {
+        // set setSelected to false otherwise it will trigger callback.
+        assert mUserIdManager != null;
+        mTabs.addTab(createTab(
+                getEnterpriseString(PERSONAL_TAB, R.string.personal_tab),
+                mUserIdManager.getSystemUser()), /* setSelected= */false);
+        mTabs.addTab(createTab(
+                getEnterpriseString(WORK_TAB, R.string.work_tab),
+                mUserIdManager.getManagedUser()), /* setSelected= */false);
+    }
+
     private String getEnterpriseString(String updatableStringId, int defaultStringId) {
         if (SdkLevel.isAtLeastT()) {
             return getUpdatableEnterpriseString(updatableStringId, defaultStringId);
diff --git a/src/com/android/documentsui/UserManagerState.java b/src/com/android/documentsui/UserManagerState.java
index 90cf017..6935fa5 100644
--- a/src/com/android/documentsui/UserManagerState.java
+++ b/src/com/android/documentsui/UserManagerState.java
@@ -241,10 +241,12 @@
                 if (userHandle.getIdentifier() == ActivityManager.getCurrentUser()) {
                     result.add(UserId.of(userHandle));
                 } else {
-                    final UserProperties userProperties =
-                            mUserManager.getUserProperties(userHandle);
-                    if (userProperties.getShowInSharingSurfaces()
-                            == UserProperties.SHOW_IN_SHARING_SURFACES_SEPARATE) {
+                    // Out of all the profiles returned by user manager the profiles that are
+                    // returned should satisfy both the following conditions:
+                    // 1. It has user property SHOW_IN_SHARING_SURFACES_SEPARATE
+                    // 2. Quite mode is not enabled, if it is enabled then the profile's user
+                    //    property is not SHOW_IN_QUIET_MODE_HIDDEN
+                    if (isProfileAllowed(userHandle)) {
                         result.add(UserId.of(userHandle));
                     }
                 }
@@ -254,6 +256,19 @@
             }
         }
 
+        @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+        private boolean isProfileAllowed(UserHandle userHandle) {
+            final UserProperties userProperties =
+                    mUserManager.getUserProperties(userHandle);
+            if (userProperties.getShowInSharingSurfaces()
+                    == UserProperties.SHOW_IN_SHARING_SURFACES_SEPARATE) {
+                return !UserId.of(userHandle).isQuietModeEnabled(mContext)
+                        || userProperties.getShowInQuietMode()
+                        != UserProperties.SHOW_IN_QUIET_MODE_HIDDEN;
+            }
+            return false;
+        }
+
         private void getUserIdsInternalPreV(List<UserHandle> userProfiles, List<UserId> result) {
             result.add(mCurrentUser);
             UserId systemUser = null;
diff --git a/src/com/android/documentsui/base/UserId.java b/src/com/android/documentsui/base/UserId.java
index 642ebe6..2184291 100644
--- a/src/com/android/documentsui/base/UserId.java
+++ b/src/com/android/documentsui/base/UserId.java
@@ -149,8 +149,8 @@
      * Returns true if the this user is in quiet mode.
      */
     public boolean isQuietModeEnabled(Context context) {
-        final UserManager userManager =
-                (UserManager) context.getSystemService(Context.USER_SERVICE);
+        final UserManager userManager = context.getSystemService(UserManager.class);
+        assert userManager != null;
         return userManager.isQuietModeEnabled(mUserHandle);
     }
 
diff --git a/src/com/android/documentsui/files/FilesActivity.java b/src/com/android/documentsui/files/FilesActivity.java
index c426a70..ddb8b10 100644
--- a/src/com/android/documentsui/files/FilesActivity.java
+++ b/src/com/android/documentsui/files/FilesActivity.java
@@ -61,6 +61,7 @@
 import com.android.documentsui.sidebar.RootsFragment;
 import com.android.documentsui.ui.DialogController;
 import com.android.documentsui.ui.MessageBuilder;
+import com.android.documentsui.util.FeatureFlagUtils;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -83,8 +84,15 @@
     }
 
     // make these methods visible in this package to work around compiler bug http://b/62218600
-    @Override protected boolean focusSidebar() { return super.focusSidebar(); }
-    @Override protected boolean popDir() { return super.popDir(); }
+    @Override
+    protected boolean focusSidebar() {
+        return super.focusSidebar();
+    }
+
+    @Override
+    protected boolean popDir() {
+        return super.popDir();
+    }
 
     @Override
     public void onCreate(Bundle icicle) {
@@ -157,8 +165,7 @@
                 mInjector.selectionMgr,
                 mProfileTabsAddonsStub);
 
-        mAppsRowManager = new AppsRowManager(mInjector.actions, mState.supportsCrossProfile(),
-                mUserIdManager);
+        mAppsRowManager = getAppsRowManager();
         mInjector.appsRowManager = mAppsRowManager;
 
         mActivityInputHandler =
@@ -194,6 +201,14 @@
         presentFileErrors(icicle, intent);
     }
 
+    private AppsRowManager getAppsRowManager() {
+        return FeatureFlagUtils.isPrivateSpaceEnabled()
+                ? new AppsRowManager(mInjector.actions, mState.supportsCrossProfile(),
+                mUserManagerState)
+                : new AppsRowManager(mInjector.actions, mState.supportsCrossProfile(),
+                        mUserIdManager);
+    }
+
     // This is called in the intent contains label and icon resources.
     // When that is true, the launcher activity has supplied them so we
     // can adapt our presentation to how we were launched.
@@ -207,11 +222,11 @@
     // called Downloads, which is also not the desired behavior.
     private void updateTaskDescription(final Intent intent) {
         int labelRes = intent.getIntExtra(LauncherActivity.TASK_LABEL_RES, -1);
-        assert(labelRes > -1);
+        assert (labelRes > -1);
         String label = getResources().getString(labelRes);
 
         int iconRes = intent.getIntExtra(LauncherActivity.TASK_ICON_RES, -1);
-        assert(iconRes > -1);
+        assert (iconRes > -1);
 
         setTaskDescription(new TaskDescription(label, iconRes));
     }
@@ -244,14 +259,14 @@
         final Intent intent = getIntent();
 
         // This is a remnant of old logic where we used to initialize accept MIME types in
-        // BaseActivity. ProvidersAccess still rely on this being correctly initialized so we still have
-        // to initialize it in FilesActivity.
+        // BaseActivity. ProvidersAccess still rely on this being correctly initialized, so we
+        // still have to initialize it in FilesActivity.
         state.initAcceptMimes(intent, "*/*");
         state.action = State.ACTION_BROWSE;
         state.allowMultiple = true;
 
         // Options specific to the DocumentsActivity.
-        assert(!intent.hasExtra(Intent.EXTRA_LOCAL_ONLY));
+        assert (!intent.hasExtra(Intent.EXTRA_LOCAL_ONLY));
     }
 
     @Override
@@ -333,7 +348,7 @@
         final RootInfo root = getCurrentRoot();
         final DocumentInfo cwd = getCurrentDirectory();
 
-        assert(!mSearchManager.isSearching());
+        assert (!mSearchManager.isSearching());
 
         if (mState.stack.isRecents()) {
             DirectoryFragment.showRecentsOpen(fm, anim);
@@ -355,7 +370,7 @@
 
     @Override
     public void onDirectoryCreated(DocumentInfo doc) {
-        assert(doc.isDirectory());
+        assert (doc.isDirectory());
         mInjector.focusManager.focusDocument(doc.documentId);
     }
 
diff --git a/src/com/android/documentsui/picker/PickActivity.java b/src/com/android/documentsui/picker/PickActivity.java
index 13b8237..c1886f3 100644
--- a/src/com/android/documentsui/picker/PickActivity.java
+++ b/src/com/android/documentsui/picker/PickActivity.java
@@ -444,20 +444,13 @@
 
     private boolean canShare(List<DocumentInfo> docs) {
         for (DocumentInfo doc : docs) {
-            if (!canInteractWith(doc.userId)) {
+            if (!mState.canInteractWith(doc.userId)) {
                 return false;
             }
         }
         return true;
     }
 
-    private boolean canInteractWith(UserId userId) {
-        if (FeatureFlagUtils.isPrivateSpaceEnabled()) {
-            return mState.canForwardToProfileIdMap.getOrDefault(userId, false);
-        }
-        return mState.canInteractWith(userId);
-    }
-
     @CallSuper
     @Override
     public boolean onKeyDown(int keyCode, KeyEvent event) {
diff --git a/src/com/android/documentsui/roots/ProvidersCache.java b/src/com/android/documentsui/roots/ProvidersCache.java
index 15f346b..e822325 100644
--- a/src/com/android/documentsui/roots/ProvidersCache.java
+++ b/src/com/android/documentsui/roots/ProvidersCache.java
@@ -138,7 +138,10 @@
         mUserManagerState = userManagerState;
     }
 
-    private RootInfo generateRecentsRoot(UserId rootUserId) {
+    /**
+     * Generates recent root for the provided user id
+     */
+    public RootInfo generateRecentsRoot(UserId rootUserId) {
         return new RootInfo() {{
             // Special root for recents
             userId = rootUserId;
diff --git a/src/com/android/documentsui/sidebar/RootsFragment.java b/src/com/android/documentsui/sidebar/RootsFragment.java
index fb8d2fc..40d14b9 100644
--- a/src/com/android/documentsui/sidebar/RootsFragment.java
+++ b/src/com/android/documentsui/sidebar/RootsFragment.java
@@ -432,30 +432,53 @@
 
         final List<Item> rootList = new ArrayList<>();
         final List<Item> rootListOtherUser = new ArrayList<>();
+        final List<List<Item>> rootListAllUsers = new ArrayList<>();
+        for (int i = 0; i < userIds.size(); ++i) {
+            rootListAllUsers.add(new ArrayList<>());
+        }
+
         mApplicationItemList = new ArrayList<>();
         if (handlerAppIntent != null) {
             includeHandlerApps(state, handlerAppIntent, excludePackage, rootList, rootListOtherUser,
-                    otherProviders, userIds, maybeShowBadge);
+                    rootListAllUsers, otherProviders, userIds, maybeShowBadge);
         } else {
             // Only add providers
-            Collections.sort(otherProviders, comp);
+            otherProviders.sort(comp);
             for (RootItem item : otherProviders) {
-                if (UserId.CURRENT_USER.equals(item.userId)) {
-                    rootList.add(item);
+                if (FeatureFlagUtils.isPrivateSpaceEnabled()) {
+                    createRootListsPrivateSpaceEnabled(item, userIds, rootListAllUsers);
                 } else {
-                    rootListOtherUser.add(item);
+                    createRootListsPrivateSpaceDisabled(item, rootList, rootListOtherUser);
                 }
                 mApplicationItemList.add(item);
             }
         }
 
-        List<Item> presentableList = new UserItemsCombiner(
-                context.getResources(), context.getSystemService(DevicePolicyManager.class), state)
+        List<Item> presentableList =
+                FeatureFlagUtils.isPrivateSpaceEnabled() ? getPresentableListPrivateSpaceEnabled(
+                        context, state, rootListAllUsers, userIds) :
+                        getPresentableListPrivateSpaceDisabled(context, state, rootList,
+                                rootListOtherUser);
+        addListToResult(result, presentableList);
+        return result;
+    }
+
+    private List<Item> getPresentableListPrivateSpaceEnabled(Context context, State state,
+            List<List<Item>> rootListAllUsers, List<UserId> userIds) {
+        return new UserItemsCombiner(context.getResources(),
+                context.getSystemService(DevicePolicyManager.class), state)
+                .setRootListForAllUsers(rootListAllUsers)
+                .createPresentableListForAllUsers(userIds,
+                        DocumentsApplication.getUserManagerState(context).getUserIdToLabelMap());
+    }
+
+    private List<Item> getPresentableListPrivateSpaceDisabled(Context context, State state,
+            List<Item> rootList, List<Item> rootListOtherUser) {
+        return new UserItemsCombiner(context.getResources(),
+                context.getSystemService(DevicePolicyManager.class), state)
                 .setRootListForCurrentUser(rootList)
                 .setRootListForOtherUser(rootListOtherUser)
                 .createPresentableList();
-        addListToResult(result, presentableList);
-        return result;
     }
 
     private void addListToResult(List<Item> result, List<Item> rootList) {
@@ -471,8 +494,8 @@
      */
     private void includeHandlerApps(State state,
             Intent handlerAppIntent, @Nullable String excludePackage, List<Item> rootList,
-            List<Item> rootListOtherUser, List<RootItem> otherProviders, List<UserId> userIds,
-            boolean maybeShowBadge) {
+            List<Item> rootListOtherUser, List<List<Item>> rootListAllUsers,
+            List<RootItem> otherProviders, List<UserId> userIds, boolean maybeShowBadge) {
         if (VERBOSE) Log.v(TAG, "Adding handler apps for intent: " + handlerAppIntent);
 
         Context context = getContext();
@@ -525,29 +548,49 @@
                 item = rootItem;
             }
 
-            if (UserId.CURRENT_USER.equals(item.userId)) {
-                if (VERBOSE) Log.v(TAG, "Adding provider : " + item);
-                rootList.add(item);
+            if (FeatureFlagUtils.isPrivateSpaceEnabled()) {
+                createRootListsPrivateSpaceEnabled(item, userIds, rootListAllUsers);
             } else {
-                if (VERBOSE) Log.v(TAG, "Adding provider to other users : " + item);
-                rootListOtherUser.add(item);
+                createRootListsPrivateSpaceDisabled(item, rootList, rootListOtherUser);
             }
         }
 
         for (Item item : appItems.values()) {
-            if (UserId.CURRENT_USER.equals(item.userId)) {
-                rootList.add(item);
+            if (FeatureFlagUtils.isPrivateSpaceEnabled()) {
+                createRootListsPrivateSpaceEnabled(item, userIds, rootListAllUsers);
             } else {
-                rootListOtherUser.add(item);
+                createRootListsPrivateSpaceDisabled(item, rootList, rootListOtherUser);
             }
         }
 
         final String preferredRootPackage = getResources().getString(
                 R.string.preferred_root_package, "");
         final ItemComparator comp = new ItemComparator(preferredRootPackage);
-        Collections.sort(rootList, comp);
-        Collections.sort(rootListOtherUser, comp);
 
+        if (FeatureFlagUtils.isPrivateSpaceEnabled()) {
+            addToApplicationItemListPrivateSpaceEnabled(userIds, rootListAllUsers, comp, state);
+        } else {
+            addToApplicationItemListPrivateSpaceDisabled(rootList, rootListOtherUser, comp, state);
+        }
+
+    }
+
+    private void addToApplicationItemListPrivateSpaceEnabled(List<UserId> userIds,
+            List<List<Item>> rootListAllUsers, ItemComparator comp, State state) {
+        for (int i = 0; i < userIds.size(); ++i) {
+            rootListAllUsers.get(i).sort(comp);
+            if (UserId.CURRENT_USER.equals(userIds.get(i))) {
+                mApplicationItemList.addAll(rootListAllUsers.get(i));
+            } else if (state.supportsCrossProfile && state.canInteractWith(userIds.get(i))) {
+                mApplicationItemList.addAll(rootListAllUsers.get(i));
+            }
+        }
+    }
+
+    private void addToApplicationItemListPrivateSpaceDisabled(List<Item> rootList,
+            List<Item> rootListOtherUser, ItemComparator comp, State state) {
+        rootList.sort(comp);
+        rootListOtherUser.sort(comp);
         if (state.supportsCrossProfile() && state.canShareAcrossProfile) {
             mApplicationItemList.addAll(rootList);
             mApplicationItemList.addAll(rootListOtherUser);
@@ -556,6 +599,25 @@
         }
     }
 
+    private void createRootListsPrivateSpaceEnabled(Item item, List<UserId> userIds,
+            List<List<Item>> rootListAllUsers) {
+        for (int i = 0; i < userIds.size(); ++i) {
+            if (userIds.get(i).equals(item.userId)) {
+                rootListAllUsers.get(i).add(item);
+                break;
+            }
+        }
+    }
+
+    private void createRootListsPrivateSpaceDisabled(Item item, List<Item> rootList,
+            List<Item> rootListOtherUser) {
+        if (UserId.CURRENT_USER.equals(item.userId)) {
+            rootList.add(item);
+        } else {
+            rootListOtherUser.add(item);
+        }
+    }
+
     @Override
     public void onResume() {
         super.onResume();
diff --git a/src/com/android/documentsui/sidebar/UserItemsCombiner.java b/src/com/android/documentsui/sidebar/UserItemsCombiner.java
index 1a68ca7..564a511 100644
--- a/src/com/android/documentsui/sidebar/UserItemsCombiner.java
+++ b/src/com/android/documentsui/sidebar/UserItemsCombiner.java
@@ -36,6 +36,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 
 /**
  * Converts user-specific lists of items into a single merged list appropriate for displaying in the
@@ -49,6 +50,7 @@
     private final State mState;
     private List<Item> mRootList;
     private List<Item> mRootListOtherUser;
+    private List<List<Item>> mRootListAllUsers;
 
     UserItemsCombiner(Resources resources, DevicePolicyManager dpm, State state) {
         mCurrentUser = UserId.CURRENT_USER;
@@ -73,6 +75,11 @@
         return this;
     }
 
+    UserItemsCombiner setRootListForAllUsers(List<List<Item>> listOfRootLists) {
+        mRootListAllUsers = checkNotNull(listOfRootLists);
+        return this;
+    }
+
     /**
      * Returns a combined lists from the provided lists. {@link HeaderItem}s indicating profile
      * will be added if the list of current user and the other user are not empty.
@@ -109,6 +116,52 @@
         return result;
     }
 
+    public List<Item> createPresentableListForAllUsers(List<UserId> userIds,
+            Map<UserId, String> userIdToLabelMap) {
+
+        checkArgument(mRootListAllUsers != null, "RootListForAllUsers is not set");
+
+        final List<Item> result = new ArrayList<>();
+        if (mState.supportsCrossProfile()) {
+            // headerItemList will hold headers for userIds that are accessible, and
+            final List<Item> headerItemList = new ArrayList<>();
+            int accessibleProfilesCount = 0;
+            for (int i = 0; i < userIds.size(); ++i) {
+                // The received user id list contains all users present on the device,
+                // the headerItemList will contain header item or null at the same index as
+                // the user id in the received list
+                if (mState.canInteractWith(userIds.get(i))
+                        && !mRootListAllUsers.get(i).isEmpty()) {
+                    accessibleProfilesCount += 1;
+                    headerItemList.add(new HeaderItem(userIdToLabelMap.get(userIds.get(i))));
+                } else {
+                    headerItemList.add(null);
+                }
+            }
+            // Do not add header item if:
+            // 1. only the current profile is accessible
+            // 2. only one profile has non-empty root item list
+            if (accessibleProfilesCount == 1) {
+                for (int i = 0; i < userIds.size(); ++i) {
+                    if (headerItemList.get(i) == null) continue;
+                    result.addAll(mRootListAllUsers.get(i));
+                    break;
+                }
+            } else {
+                for (int i = 0; i < userIds.size(); ++i) {
+                    // Since the header item and the corresponding accessible user id share the same
+                    // index we add the user id along with its non-null header to the result.
+                    if (headerItemList.get(i) == null) continue;
+                    result.add(headerItemList.get(i));
+                    result.addAll(mRootListAllUsers.get(i));
+                }
+            }
+        } else {
+            result.addAll(mRootListAllUsers.get(userIds.indexOf(mCurrentUser)));
+        }
+        return result;
+    }
+
     private String getEnterpriseString(String updatableStringId, int defaultStringId) {
         if (SdkLevel.isAtLeastT()) {
             return getUpdatableEnterpriseString(updatableStringId, defaultStringId);
diff --git a/tests/common/com/android/documentsui/TestActivity.java b/tests/common/com/android/documentsui/TestActivity.java
index eb321fa..5aa3faa 100644
--- a/tests/common/com/android/documentsui/TestActivity.java
+++ b/tests/common/com/android/documentsui/TestActivity.java
@@ -260,6 +260,14 @@
     }
 
     @Override
+    public final String getSystemServiceName(Class<?> serviceName) {
+        if (serviceName == UserManager.class) {
+            return Context.USER_SERVICE;
+        }
+        throw new IllegalArgumentException("Unknown service name " + serviceName);
+    }
+
+    @Override
     public final void finish() {
         finishedHandler.accept(null);
     }
diff --git a/tests/common/com/android/documentsui/testing/TestProvidersAccess.java b/tests/common/com/android/documentsui/testing/TestProvidersAccess.java
index 4fae7a7..554c42c 100644
--- a/tests/common/com/android/documentsui/testing/TestProvidersAccess.java
+++ b/tests/common/com/android/documentsui/testing/TestProvidersAccess.java
@@ -229,6 +229,67 @@
         }
     }
 
+    public static class AnotherUser {
+        public static final UserHandle USER_HANDLE = UserHandle.of(
+                TestProvidersAccess.USER_ID.getIdentifier() + 2);
+        public static final UserId USER_ID = UserId.of(AnotherUser.USER_HANDLE);
+
+        public static final RootInfo DOWNLOADS;
+        public static final RootInfo HOME;
+        public static final RootInfo IMAGE;
+        public static final RootInfo PICKLES;
+        public static final RootInfo MTP_ROOT;
+
+        static {
+            UserId userId = AnotherUser.USER_ID;
+
+            DOWNLOADS = new RootInfo();
+            DOWNLOADS.userId = userId;
+            DOWNLOADS.authority = Providers.AUTHORITY_DOWNLOADS;
+            DOWNLOADS.rootId = Providers.ROOT_ID_DOWNLOADS;
+            DOWNLOADS.title = "Downloads";
+            DOWNLOADS.derivedType = RootInfo.TYPE_DOWNLOADS;
+            DOWNLOADS.flags = Root.FLAG_LOCAL_ONLY
+                    | Root.FLAG_SUPPORTS_CREATE
+                    | Root.FLAG_SUPPORTS_RECENTS;
+
+            HOME = new RootInfo();
+            HOME.userId = userId;
+            HOME.authority = Providers.AUTHORITY_STORAGE;
+            HOME.rootId = Providers.ROOT_ID_HOME;
+            HOME.title = "Home";
+            HOME.derivedType = RootInfo.TYPE_LOCAL;
+            HOME.flags = Root.FLAG_LOCAL_ONLY
+                    | Root.FLAG_SUPPORTS_CREATE
+                    | Root.FLAG_SUPPORTS_IS_CHILD
+                    | Root.FLAG_SUPPORTS_RECENTS;
+
+            IMAGE = new RootInfo();
+            IMAGE.userId = userId;
+            IMAGE.authority = Providers.AUTHORITY_MEDIA;
+            IMAGE.rootId = Providers.ROOT_ID_IMAGES;
+            IMAGE.title = "Images";
+            IMAGE.derivedType = RootInfo.TYPE_IMAGES;
+
+            PICKLES = new RootInfo();
+            PICKLES.userId = userId;
+            PICKLES.authority = "yummies";
+            PICKLES.rootId = "pickles";
+            PICKLES.title = "Pickles";
+            PICKLES.summary = "Yummy pickles";
+
+            MTP_ROOT = new RootInfo();
+            MTP_ROOT.userId = userId;
+            MTP_ROOT.authority = Providers.AUTHORITY_MTP;
+            MTP_ROOT.rootId = Providers.ROOT_ID_DOCUMENTS;
+            MTP_ROOT.title = "MTP";
+            MTP_ROOT.derivedType = RootInfo.TYPE_MTP;
+            MTP_ROOT.flags = Root.FLAG_SUPPORTS_CREATE
+                    | Root.FLAG_LOCAL_ONLY
+                    | Root.FLAG_SUPPORTS_IS_CHILD;
+        }
+    }
+
     public final Map<String, Collection<RootInfo>> roots = new HashMap<>();
     private @Nullable RootInfo nextRoot;
 
diff --git a/tests/unit/com/android/documentsui/AbstractActionHandlerTest.java b/tests/unit/com/android/documentsui/AbstractActionHandlerTest.java
index 3eb8ab2..35acdff 100644
--- a/tests/unit/com/android/documentsui/AbstractActionHandlerTest.java
+++ b/tests/unit/com/android/documentsui/AbstractActionHandlerTest.java
@@ -32,7 +32,6 @@
 
 import androidx.recyclerview.selection.ItemDetailsLookup.ItemDetails;
 import androidx.test.filters.MediumTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.documentsui.base.DocumentStack;
 import com.android.documentsui.base.EventListener;
@@ -48,10 +47,16 @@
 import com.android.documentsui.testing.TestEventHandler;
 import com.android.documentsui.testing.TestProvidersAccess;
 import com.android.documentsui.testing.UserManagers;
+import com.android.documentsui.util.FeatureFlagUtils;
+
+import com.google.android.collect.Lists;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
 
 import java.util.Arrays;
 import java.util.concurrent.CountDownLatch;
@@ -60,7 +65,7 @@
 /**
  * A unit test *for* AbstractActionHandler, not an abstract test baseclass.
  */
-@RunWith(AndroidJUnit4.class)
+@RunWith(Parameterized.class)
 @MediumTest
 public class AbstractActionHandlerTest {
 
@@ -68,11 +73,26 @@
     private TestEnv mEnv;
     private AbstractActionHandler<TestActivity> mHandler;
 
+    @Parameter(0)
+    public boolean isPrivateSpaceEnabled;
+
+    /**
+     * Parametrize values for {@code isPrivateSpaceEnabled} to run all the tests twice once with
+     * private space flag enabled and once with it disabled.
+     */
+    @Parameters(name = "privateSpaceEnabled={0}")
+    public static Iterable<?> data() {
+        return Lists.newArrayList(true, false);
+    }
+
     @Before
     public void setUp() {
         mEnv = TestEnv.create();
         mActivity = TestActivity.create(mEnv);
         mActivity.userManager = UserManagers.create();
+        if (FeatureFlagUtils.isPrivateSpaceEnabled()) {
+            mEnv.state.canForwardToProfileIdMap.put(TestProvidersAccess.USER_ID, true);
+        }
         mHandler = new AbstractActionHandler<TestActivity>(
                 mActivity,
                 mEnv.state,
@@ -281,7 +301,11 @@
     @Test
     public void testCrossProfileDocuments_success() throws Exception {
         mEnv.state.action = State.ACTION_GET_CONTENT;
-        mEnv.state.canShareAcrossProfile = true;
+        if (FeatureFlagUtils.isPrivateSpaceEnabled()) {
+            mEnv.state.canForwardToProfileIdMap.put(TestProvidersAccess.OtherUser.USER_ID, true);
+        } else {
+            mEnv.state.canShareAcrossProfile = true;
+        }
         mEnv.state.stack.changeRoot(TestProvidersAccess.OtherUser.HOME);
         mEnv.state.stack.push(TestEnv.OtherUser.FOLDER_0);
 
@@ -308,7 +332,11 @@
     @Test
     public void testLoadCrossProfileDoc_failsWithQuietModeException() throws Exception {
         mEnv.state.action = State.ACTION_GET_CONTENT;
-        mEnv.state.canShareAcrossProfile = true;
+        if (FeatureFlagUtils.isPrivateSpaceEnabled()) {
+            mEnv.state.canForwardToProfileIdMap.put(TestProvidersAccess.OtherUser.USER_ID, true);
+        } else {
+            mEnv.state.canShareAcrossProfile = true;
+        }
         mEnv.state.stack.changeRoot(TestProvidersAccess.OtherUser.HOME);
         mEnv.state.stack.push(TestEnv.OtherUser.FOLDER_0);
         // Turn off the other user.
@@ -389,7 +417,11 @@
                 .setNextChildDocumentsReturns(TestEnv.OtherUser.FILE_PNG);
 
         // Disallow sharing across profile
-        mEnv.state.canShareAcrossProfile = false;
+        if (FeatureFlagUtils.isPrivateSpaceEnabled()) {
+            mEnv.state.canForwardToProfileIdMap.put(TestProvidersAccess.OtherUser.USER_ID, false);
+        } else {
+            mEnv.state.canShareAcrossProfile = false;
+        }
 
         TestEventHandler<Model.Update> listener = new TestEventHandler<>();
         mEnv.model.addUpdateListener(listener::accept);
@@ -404,7 +436,11 @@
                 .isInstanceOf(CrossProfileNoPermissionException.class);
 
         // Allow sharing across profile.
-        mEnv.state.canShareAcrossProfile = true;
+        if (FeatureFlagUtils.isPrivateSpaceEnabled()) {
+            mEnv.state.canForwardToProfileIdMap.put(TestProvidersAccess.OtherUser.USER_ID, true);
+        } else {
+            mEnv.state.canShareAcrossProfile = true;
+        }
 
         CountDownLatch latch2 = new CountDownLatch(1);
         mEnv.model.addUpdateListener(update -> latch2.countDown());
diff --git a/tests/unit/com/android/documentsui/DocumentsAccessTest.java b/tests/unit/com/android/documentsui/DocumentsAccessTest.java
index 8a08d43..300a678 100644
--- a/tests/unit/com/android/documentsui/DocumentsAccessTest.java
+++ b/tests/unit/com/android/documentsui/DocumentsAccessTest.java
@@ -18,10 +18,12 @@
 
 import static junit.framework.Assert.fail;
 
+import static org.mockito.Mockito.mock;
+
+import android.content.ContentProviderClient;
 import android.content.pm.PackageManager;
 
 import androidx.test.filters.MediumTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.documentsui.testing.TestEnv;
 import com.android.documentsui.testing.TestProvidersAccess;
@@ -31,14 +33,24 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
 
-@RunWith(AndroidJUnit4.class)
+@RunWith(Parameterized.class)
 @MediumTest
 public class DocumentsAccessTest {
 
     private TestActivity mActivity;
     private DocumentsAccess mDocumentsAccess;
     private TestEnv mEnv;
+    private ContentProviderClient mMockContentProviderClient = mock(ContentProviderClient.class);
+
+    @Parameterized.Parameter(0)
+    public boolean isPrivateSpaceEnabled;
+
+    @Parameterized.Parameters(name = "privateSpaceEnabled={0}")
+    public static Iterable<?> data() {
+        return com.google.android.collect.Lists.newArrayList(true, false);
+    }
 
     @Before
     public void setUp() throws PackageManager.NameNotFoundException {
diff --git a/tests/unit/com/android/documentsui/ProfileTabsTest.java b/tests/unit/com/android/documentsui/ProfileTabsTest.java
index 373f4e5..48250dd 100644
--- a/tests/unit/com/android/documentsui/ProfileTabsTest.java
+++ b/tests/unit/com/android/documentsui/ProfileTabsTest.java
@@ -18,6 +18,8 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.junit.Assume.assumeTrue;
+
 import android.content.Context;
 import android.net.Uri;
 import android.os.UserHandle;
@@ -32,20 +34,28 @@
 import com.android.documentsui.base.UserId;
 import com.android.documentsui.testing.TestEnv;
 import com.android.documentsui.testing.TestProvidersAccess;
+import com.android.documentsui.util.FeatureFlagUtils;
+import com.android.modules.utils.build.SdkLevel;
 
-import com.google.android.collect.Lists;
 import com.google.android.material.tabs.TabLayout;
+import com.google.common.collect.Lists;
 
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
 
-import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 
+@RunWith(Parameterized.class)
 public class ProfileTabsTest {
 
-    private final UserId systemUser = UserId.of(UserHandle.SYSTEM);
-    private final UserId managedUser = UserId.of(100);
+    private final UserId mSystemUser = UserId.of(UserHandle.SYSTEM);
+    private final UserId mManagedUser = UserId.of(100);
+    private final UserId mPrivateUser = UserId.of(101);
 
     private ProfileTabs mProfileTabs;
 
@@ -55,9 +65,22 @@
     private TestEnvironment mTestEnv;
     private State mState;
     private TestUserIdManager mTestUserIdManager;
+    private TestUserManagerState mTestUserManagerState;
     private TestCommonAddons mTestCommonAddons;
     private boolean mIsListenerInvoked;
 
+    @Parameter(0)
+    public boolean isPrivateSpaceEnabled;
+
+    /**
+     * Parametrize values for {@code isPrivateSpaceEnabled} to run all the tests twice once with
+     * private space flag enabled and once with it disabled.
+     */
+    @Parameters(name = "privateSpaceEnabled={0}")
+    public static Iterable<?> data() {
+        return Lists.newArrayList(true, false);
+    }
+
     @Before
     public void setUp() {
         TestEnv.create();
@@ -76,14 +99,19 @@
         mTestEnv = new TestEnvironment();
         mTestEnv.isSearchExpanded = false;
 
-        mTestUserIdManager = new TestUserIdManager();
+        if (FeatureFlagUtils.isPrivateSpaceEnabled()) {
+            mTestUserManagerState = new TestUserManagerState();
+        } else {
+            mTestUserIdManager = new TestUserIdManager();
+        }
+
         mTestCommonAddons = new TestCommonAddons();
         mTestCommonAddons.mCurrentRoot = TestProvidersAccess.DOWNLOADS;
     }
 
     @Test
     public void testUpdateView_singleUser_shouldHide() {
-        initializeWithUsers(systemUser);
+        initializeWithUsers(FeatureFlagUtils.isPrivateSpaceEnabled(), mSystemUser);
 
         assertThat(mTabLayoutContainer.getVisibility()).isEqualTo(View.GONE);
         assertThat(mTabLayout.getTabCount()).isEqualTo(0);
@@ -91,23 +119,40 @@
 
     @Test
     public void testUpdateView_twoUsers_shouldShow() {
-        initializeWithUsers(systemUser, managedUser);
+        initializeWithUsers(FeatureFlagUtils.isPrivateSpaceEnabled(), mSystemUser, mManagedUser);
 
         assertThat(mTabLayoutContainer.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mTabLayout.getTabCount()).isEqualTo(2);
 
         TabLayout.Tab tab1 = mTabLayout.getTabAt(0);
-        assertThat(tab1.getTag()).isEqualTo(systemUser);
+        assertThat(tab1.getTag()).isEqualTo(mSystemUser);
         assertThat(tab1.getText()).isEqualTo(mContext.getString(R.string.personal_tab));
 
         TabLayout.Tab tab2 = mTabLayout.getTabAt(1);
-        assertThat(tab2.getTag()).isEqualTo(managedUser);
+        assertThat(tab2.getTag()).isEqualTo(mManagedUser);
         assertThat(tab2.getText()).isEqualTo(mContext.getString(R.string.work_tab));
     }
 
     @Test
+    public void testUpdateView_multiUsers_shouldShow() {
+        assumeTrue(SdkLevel.isAtLeastV() && FeatureFlagUtils.isPrivateSpaceEnabled());
+        initializeWithUsers(true, mSystemUser, mManagedUser, mPrivateUser);
+
+        assertThat(mTabLayoutContainer.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mTabLayout.getTabCount()).isEqualTo(3);
+
+        final Map<UserId, String> userIdToLabelMap = mTestUserManagerState.getUserIdToLabelMap();
+        for (int i = 0; i < 3; ++i) {
+            TabLayout.Tab tab = mTabLayout.getTabAt(i);
+            assertThat(tab).isNotNull();
+            UserId userId = (UserId) tab.getTag();
+            assertThat(tab.getText()).isEqualTo(userIdToLabelMap.get(userId));
+        }
+    }
+
+    @Test
     public void testUpdateView_twoUsers_doesNotSupportCrossProfile_shouldHide() {
-        initializeWithUsers(systemUser, managedUser);
+        initializeWithUsers(FeatureFlagUtils.isPrivateSpaceEnabled(), mSystemUser, mManagedUser);
 
         mState.supportsCrossProfile = false;
         mProfileTabs.updateView();
@@ -117,7 +162,7 @@
 
     @Test
     public void testUpdateView_twoUsers_subFolder_shouldHide() {
-        initializeWithUsers(systemUser, managedUser);
+        initializeWithUsers(FeatureFlagUtils.isPrivateSpaceEnabled(), mSystemUser, mManagedUser);
 
         // Push 1 more folder. Now the stack has size of 2.
         mState.stack.push(TestEnv.FOLDER_1);
@@ -129,7 +174,7 @@
 
     @Test
     public void testUpdateView_twoUsers_recents_subFolder_shouldHide() {
-        initializeWithUsers(systemUser, managedUser);
+        initializeWithUsers(FeatureFlagUtils.isPrivateSpaceEnabled(), mSystemUser, mManagedUser);
 
         mState.stack.changeRoot(TestProvidersAccess.RECENTS);
         // This(stack of size 2 in Recents) may not happen in real world.
@@ -142,7 +187,7 @@
 
     @Test
     public void testUpdateView_twoUsers_thirdParty_shouldHide() {
-        initializeWithUsers(systemUser, managedUser);
+        initializeWithUsers(FeatureFlagUtils.isPrivateSpaceEnabled(), mSystemUser, mManagedUser);
 
         mState.stack.changeRoot(TestProvidersAccess.PICKLES);
         mState.stack.push((TestEnv.FOLDER_0));
@@ -155,7 +200,7 @@
     @Test
     public void testUpdateView_twoUsers_isSearching_shouldHide() {
         mTestEnv.isSearchExpanded = true;
-        initializeWithUsers(systemUser, managedUser);
+        initializeWithUsers(FeatureFlagUtils.isPrivateSpaceEnabled(), mSystemUser, mManagedUser);
 
         assertThat(mTabLayoutContainer.getVisibility()).isEqualTo(View.GONE);
         assertThat(mTabLayout.getTabCount()).isEqualTo(2);
@@ -163,78 +208,147 @@
 
     @Test
     public void testUpdateView_getSelectedUser_afterUsersChanged() {
-        initializeWithUsers(systemUser, managedUser);
+        initializeWithUsers(FeatureFlagUtils.isPrivateSpaceEnabled(), mSystemUser, mManagedUser);
         mProfileTabs.updateView();
         mTabLayout.selectTab(mTabLayout.getTabAt(1));
         assertThat(mTabLayoutContainer.getVisibility()).isEqualTo(View.VISIBLE);
-        assertThat(mProfileTabs.getSelectedUser()).isEqualTo(managedUser);
+        assertThat(mProfileTabs.getSelectedUser()).isEqualTo(mManagedUser);
 
-        mTestUserIdManager.userIds = Collections.singletonList(systemUser);
+        if (FeatureFlagUtils.isPrivateSpaceEnabled()) {
+            mTestUserManagerState.userIds = Lists.newArrayList(mSystemUser);
+        } else {
+            mTestUserIdManager.userIds = Lists.newArrayList(mSystemUser);
+        }
         mProfileTabs.updateView();
         assertThat(mTabLayoutContainer.getVisibility()).isEqualTo(View.GONE);
-        assertThat(mProfileTabs.getSelectedUser()).isEqualTo(systemUser);
+        assertThat(mProfileTabs.getSelectedUser()).isEqualTo(mSystemUser);
     }
 
     @Test
     public void testUpdateView_afterCurrentRootChanged_shouldChangeSelectedUser() {
-        initializeWithUsers(systemUser, managedUser);
+        initializeWithUsers(FeatureFlagUtils.isPrivateSpaceEnabled(), mSystemUser, mManagedUser);
         mProfileTabs.updateView();
 
-        assertThat(mProfileTabs.getSelectedUser()).isEqualTo(systemUser);
+        assertThat(mProfileTabs.getSelectedUser()).isEqualTo(mSystemUser);
 
         RootInfo newRoot = RootInfo.copyRootInfo(mTestCommonAddons.mCurrentRoot);
-        newRoot.userId = managedUser;
+        newRoot.userId = mManagedUser;
         mTestCommonAddons.mCurrentRoot = newRoot;
         mProfileTabs.updateView();
 
-        assertThat(mProfileTabs.getSelectedUser()).isEqualTo(managedUser);
+        assertThat(mProfileTabs.getSelectedUser()).isEqualTo(mManagedUser);
         // updating view should not trigger listener callback.
         assertThat(mIsListenerInvoked).isFalse();
     }
 
     @Test
-    public void testgetSelectedUser_twoUsers() {
-        initializeWithUsers(systemUser, managedUser);
+    public void testUpdateView_afterCurrentRootChangedMultiUser_shouldChangeSelectedUser() {
+        assumeTrue(SdkLevel.isAtLeastV() && FeatureFlagUtils.isPrivateSpaceEnabled());
+        initializeWithUsers(true, mSystemUser, mManagedUser, mPrivateUser);
+        mProfileTabs.updateView();
+
+        assertThat(mProfileTabs.getSelectedUser()).isEqualTo(mSystemUser);
+
+        for (UserId userId : Lists.newArrayList(mManagedUser, mPrivateUser)) {
+            RootInfo newRoot = RootInfo.copyRootInfo(mTestCommonAddons.mCurrentRoot);
+            newRoot.userId = userId;
+            mTestCommonAddons.mCurrentRoot = newRoot;
+            mProfileTabs.updateView();
+
+            assertThat(mProfileTabs.getSelectedUser()).isEqualTo(userId);
+            // updating view should not trigger listener callback.
+            assertThat(mIsListenerInvoked).isFalse();
+        }
+    }
+
+    @Test
+    public void testUpdateView_afterSelectedUserBecomesUnavailable_shouldSwitchToCurrentUser() {
+        // here current user refers to UserId.CURRENT_USER, which in this case will be mSystemUser
+        assumeTrue(SdkLevel.isAtLeastV() && FeatureFlagUtils.isPrivateSpaceEnabled());
+        initializeWithUsers(true, mSystemUser, mManagedUser, mPrivateUser);
+
+        mTabLayout.selectTab(mTabLayout.getTabAt(2));
+        assertThat(mProfileTabs.getSelectedUser()).isEqualTo(mPrivateUser);
+
+        mTestUserManagerState.userIds.remove(mPrivateUser);
+        mTestUserManagerState.userIdToLabelMap.remove(mPrivateUser);
+        mProfileTabs.updateView();
+
+        assertThat(mProfileTabs.getSelectedUser()).isEqualTo(mSystemUser);
+    }
+
+    @Test
+    public void testGetSelectedUser_twoUsers() {
+        initializeWithUsers(FeatureFlagUtils.isPrivateSpaceEnabled(), mSystemUser, mManagedUser);
 
         mTabLayout.selectTab(mTabLayout.getTabAt(0));
-        assertThat(mProfileTabs.getSelectedUser()).isEqualTo(systemUser);
+        assertThat(mProfileTabs.getSelectedUser()).isEqualTo(mSystemUser);
 
         mTabLayout.selectTab(mTabLayout.getTabAt(1));
-        assertThat(mProfileTabs.getSelectedUser()).isEqualTo(managedUser);
+        assertThat(mProfileTabs.getSelectedUser()).isEqualTo(mManagedUser);
         assertThat(mIsListenerInvoked).isTrue();
     }
 
     @Test
+    public void testGetSelectedUser_multiUsers() {
+        assumeTrue(SdkLevel.isAtLeastV() && FeatureFlagUtils.isPrivateSpaceEnabled());
+        initializeWithUsers(true, mSystemUser, mManagedUser, mPrivateUser);
+
+        List<UserId> expectedProfiles = Lists.newArrayList(mSystemUser, mManagedUser, mPrivateUser);
+
+        for (int i = 0; i < 3; ++i) {
+            mTabLayout.selectTab(mTabLayout.getTabAt(i));
+            assertThat(mProfileTabs.getSelectedUser()).isEqualTo(expectedProfiles.get(i));
+            if (i == 0) continue;
+            assertThat(mIsListenerInvoked).isTrue();
+        }
+    }
+
+    @Test
     public void testReselectedUser_doesNotInvokeListener() {
-        initializeWithUsers(systemUser, managedUser);
+        initializeWithUsers(FeatureFlagUtils.isPrivateSpaceEnabled(), mSystemUser, mManagedUser);
 
         assertThat(mTabLayout.getSelectedTabPosition()).isAtLeast(0);
-        assertThat(mProfileTabs.getSelectedUser()).isEqualTo(systemUser);
+        assertThat(mProfileTabs.getSelectedUser()).isEqualTo(mSystemUser);
 
         mTabLayout.selectTab(mTabLayout.getTabAt(0));
-        assertThat(mProfileTabs.getSelectedUser()).isEqualTo(systemUser);
+        assertThat(mProfileTabs.getSelectedUser()).isEqualTo(mSystemUser);
         assertThat(mIsListenerInvoked).isFalse();
     }
 
     @Test
-    public void testgetSelectedUser_singleUsers() {
-        initializeWithUsers(systemUser);
+    public void testGetSelectedUser_singleUsers() {
+        initializeWithUsers(FeatureFlagUtils.isPrivateSpaceEnabled(), mSystemUser);
 
-        assertThat(mProfileTabs.getSelectedUser()).isEqualTo(systemUser);
+        assertThat(mProfileTabs.getSelectedUser()).isEqualTo(mSystemUser);
     }
 
-    private void initializeWithUsers(UserId... userIds) {
-        mTestUserIdManager.userIds = Lists.newArrayList(userIds);
-        for (UserId userId : userIds) {
-            if (userId.isSystem()) {
-                mTestUserIdManager.systemUser = userId;
-            } else {
-                mTestUserIdManager.managedUser = userId;
+    private void initializeWithUsers(boolean isPrivateSpaceEnabled, UserId... userIds) {
+        if (FeatureFlagUtils.isPrivateSpaceEnabled()) {
+            mTestUserManagerState.userIds = Lists.newArrayList(userIds);
+            for (UserId userId : userIds) {
+                if (userId.isSystem()) {
+                    mTestUserManagerState.userIdToLabelMap.put(userId, "Personal");
+                } else if (userId.getIdentifier() == 100) {
+                    mTestUserManagerState.userIdToLabelMap.put(userId, "Work");
+                } else {
+                    mTestUserManagerState.userIdToLabelMap.put(userId, "Private");
+                }
             }
+            mProfileTabs = new ProfileTabs(mTabLayoutContainer, mState, mTestUserManagerState,
+                    mTestEnv, mTestCommonAddons);
+        } else {
+            mTestUserIdManager.userIds = Lists.newArrayList(userIds);
+            for (UserId userId : userIds) {
+                if (userId.isSystem()) {
+                    mTestUserIdManager.systemUser = userId;
+                } else {
+                    mTestUserIdManager.managedUser = userId;
+                }
+            }
+            mProfileTabs = new ProfileTabs(mTabLayoutContainer, mState, mTestUserIdManager,
+                    mTestEnv, mTestCommonAddons);
         }
-
-        mProfileTabs = new ProfileTabs(mTabLayoutContainer, mState, mTestUserIdManager, mTestEnv,
-                mTestCommonAddons);
         mProfileTabs.updateView();
         mProfileTabs.setListener(userId -> mIsListenerInvoked = true);
     }
diff --git a/tests/unit/com/android/documentsui/sidebar/RootsFragmentTest.java b/tests/unit/com/android/documentsui/sidebar/RootsFragmentTest.java
index ea88cfb..fcfd620 100644
--- a/tests/unit/com/android/documentsui/sidebar/RootsFragmentTest.java
+++ b/tests/unit/com/android/documentsui/sidebar/RootsFragmentTest.java
@@ -28,26 +28,31 @@
 
 import androidx.test.filters.MediumTest;
 import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.documentsui.base.RootInfo;
 import com.android.documentsui.base.State;
 import com.android.documentsui.base.UserId;
 import com.android.documentsui.testing.TestProvidersAccess;
 import com.android.documentsui.testing.TestResolveInfo;
+import com.android.documentsui.util.FeatureFlagUtils;
+
+import com.google.android.collect.Lists;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
 
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
 /**
- * An unit test for RootsFragment.
+ * A unit test for RootsFragment.
  */
-@RunWith(AndroidJUnit4.class)
+@RunWith(Parameterized.class)
 @MediumTest
 public class RootsFragmentTest {
 
@@ -69,6 +74,18 @@
             TestProvidersAccess.INSPECTOR.title,
             TestProvidersAccess.PICKLES.title};
 
+    @Parameter(0)
+    public boolean isPrivateSpaceEnabled;
+
+    /**
+     * Parametrize values for {@code isPrivateSpaceEnabled} to run all the tests twice once with
+     * private space flag enabled and once with it disabled.
+     */
+    @Parameters(name = "privateSpaceEnabled={0}")
+    public static Iterable<?> data() {
+        return Lists.newArrayList(true, false);
+    }
+
     @Before
     public void setUp() {
         mContext = mock(Context.class);
@@ -83,6 +100,7 @@
 
     @Test
     public void testSortLoadResult_WithCorrectOrder() {
+        if (FeatureFlagUtils.isPrivateSpaceEnabled()) return;
         List<Item> items = mRootsFragment.sortLoadResult(
                 mContext,
                 new State(),
diff --git a/tests/unit/com/android/documentsui/sidebar/UserItemsCombinerTest.java b/tests/unit/com/android/documentsui/sidebar/UserItemsCombinerTest.java
index 2a80867..5fcd22b 100644
--- a/tests/unit/com/android/documentsui/sidebar/UserItemsCombinerTest.java
+++ b/tests/unit/com/android/documentsui/sidebar/UserItemsCombinerTest.java
@@ -30,6 +30,8 @@
 import com.android.documentsui.base.State;
 import com.android.documentsui.base.UserId;
 import com.android.documentsui.testing.TestProvidersAccess;
+import com.android.documentsui.util.FeatureFlagUtils;
+import com.android.modules.utils.build.SdkLevel;
 
 import com.google.common.collect.Lists;
 import com.google.common.truth.Correspondence;
@@ -38,8 +40,11 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 
 /**
@@ -50,6 +55,7 @@
 public class UserItemsCombinerTest {
     private static final UserId PERSONAL_USER = TestProvidersAccess.USER_ID;
     private static final UserId WORK_USER = TestProvidersAccess.OtherUser.USER_ID;
+    private static final UserId PRIVATE_USER = TestProvidersAccess.AnotherUser.USER_ID;
 
     private static final List<Item> PERSONAL_ITEMS = Lists.newArrayList(
             personalItem("personal 1"),
@@ -61,10 +67,14 @@
             workItem("work 1")
     );
 
+    private static final List<Item> PRIVATE_ITEMS = Lists.newArrayList(
+            privateItem("private1")
+    );
+
     private static final Correspondence<Item, Item> ITEM_CORRESPONDENCE =
             Correspondence.from((Item actual, Item expected) -> {
-                    return Objects.equals(actual.title, expected.title)
-                            && Objects.equals(actual.userId, expected.userId);
+                return Objects.equals(actual.title, expected.title)
+                        && Objects.equals(actual.userId, expected.userId);
             }, "has same title and userId as in");
 
     private final State mState = new State();
@@ -73,12 +83,25 @@
     private final DevicePolicyManager mDpm =
             InstrumentationRegistry.getInstrumentation().getTargetContext().getSystemService(
                     DevicePolicyManager.class);
+    private final List<UserId> mUserIds = new ArrayList<>();
+    private final Map<UserId, String> mUserIdToLabelMap = new HashMap<>();
     private UserItemsCombiner mCombiner;
 
     @Before
     public void setUp() {
         mState.canShareAcrossProfile = true;
         mState.supportsCrossProfile = true;
+        mUserIds.add(PERSONAL_USER);
+        mUserIds.add(WORK_USER);
+        mUserIdToLabelMap.put(PERSONAL_USER, "Personal");
+        mUserIdToLabelMap.put(WORK_USER, "Work");
+        mState.canForwardToProfileIdMap.put(PERSONAL_USER, true);
+        mState.canForwardToProfileIdMap.put(WORK_USER, true);
+        if (SdkLevel.isAtLeastV()) {
+            mUserIds.add(PRIVATE_USER);
+            mUserIdToLabelMap.put(PRIVATE_USER, "Private");
+            mState.canForwardToProfileIdMap.put(PRIVATE_USER, true);
+        }
     }
 
     @Test
@@ -90,6 +113,20 @@
     }
 
     @Test
+    public void testCreatePresentableListForAllUsers_empty() {
+        final List<List<Item>> rootListAllUsers = new ArrayList<>();
+        rootListAllUsers.add(Collections.emptyList());
+        rootListAllUsers.add(Collections.emptyList());
+        if (SdkLevel.isAtLeastV()) {
+            rootListAllUsers.add(Collections.emptyList());
+        }
+        mCombiner = new UserItemsCombiner(mResources, mDpm, mState)
+                .setRootListForAllUsers(rootListAllUsers);
+        assertThat(
+                mCombiner.createPresentableListForAllUsers(mUserIds, mUserIdToLabelMap)).isEmpty();
+    }
+
+    @Test
     public void testCreatePresentableList_currentIsPersonal_personalItemsOnly() {
         mCombiner = new UserItemsCombiner(mResources, mDpm, mState)
                 .setRootListForCurrentUser(PERSONAL_ITEMS)
@@ -113,6 +150,55 @@
     }
 
     @Test
+    public void testCreatePresentableListForAllUsers_currentIsPersonal_personalItemsOnly() {
+        final List<List<Item>> rootListAllUsers = new ArrayList<>();
+        rootListAllUsers.add(Lists.newArrayList(PERSONAL_ITEMS));
+        rootListAllUsers.add(Collections.emptyList());
+        if (SdkLevel.isAtLeastV()) {
+            rootListAllUsers.add(Collections.emptyList());
+        }
+        mCombiner = new UserItemsCombiner(mResources, mDpm, mState)
+                .setRootListForAllUsers(rootListAllUsers);
+        assertThat(mCombiner.createPresentableListForAllUsers(mUserIds, mUserIdToLabelMap))
+                .comparingElementsUsing(ITEM_CORRESPONDENCE)
+                .containsExactlyElementsIn(PERSONAL_ITEMS)
+                .inOrder();
+    }
+
+    @Test
+    public void testCreatePresentableListForAllUsers_currentIsWork_personalItemsOnly() {
+        final List<List<Item>> rootListAllUsers = new ArrayList<>();
+        rootListAllUsers.add(Lists.newArrayList(PERSONAL_ITEMS));
+        rootListAllUsers.add(Collections.emptyList());
+        if (SdkLevel.isAtLeastV()) {
+            rootListAllUsers.add(Collections.emptyList());
+        }
+        mCombiner = new UserItemsCombiner(mResources, mDpm, mState)
+                .overrideCurrentUserForTest(WORK_USER)
+                .setRootListForAllUsers(rootListAllUsers);
+        assertThat(mCombiner.createPresentableListForAllUsers(mUserIds, mUserIdToLabelMap))
+                .comparingElementsUsing(ITEM_CORRESPONDENCE)
+                .containsExactlyElementsIn(PERSONAL_ITEMS)
+                .inOrder();
+    }
+
+    @Test
+    public void testCreatePresentableListForAllUsers_currentIsPrivate_personalItemsOnly() {
+        if (!SdkLevel.isAtLeastV()) return;
+        final List<List<Item>> rootListAllUsers = new ArrayList<>();
+        rootListAllUsers.add(Lists.newArrayList(PERSONAL_ITEMS));
+        rootListAllUsers.add(Collections.emptyList());
+        rootListAllUsers.add(Collections.emptyList());
+        mCombiner = new UserItemsCombiner(mResources, mDpm, mState)
+                .overrideCurrentUserForTest(PRIVATE_USER)
+                .setRootListForAllUsers(rootListAllUsers);
+        assertThat(mCombiner.createPresentableListForAllUsers(mUserIds, mUserIdToLabelMap))
+                .comparingElementsUsing(ITEM_CORRESPONDENCE)
+                .containsExactlyElementsIn(PERSONAL_ITEMS)
+                .inOrder();
+    }
+
+    @Test
     public void testCreatePresentableList_currentIsPersonal_workItemsOnly() {
         mCombiner = new UserItemsCombiner(mResources, mDpm, mState)
                 .setRootListForCurrentUser(Collections.emptyList())
@@ -136,6 +222,56 @@
     }
 
     @Test
+    public void testCreatePresentableListForAllUsers_currentIsPersonal_workItemsOnly() {
+        final List<List<Item>> rootListAllUsers = new ArrayList<>();
+        rootListAllUsers.add(Collections.emptyList());
+        rootListAllUsers.add(Lists.newArrayList(WORK_ITEMS));
+        if (SdkLevel.isAtLeastV()) {
+            rootListAllUsers.add(Collections.emptyList());
+        }
+        mCombiner = new UserItemsCombiner(mResources, mDpm, mState)
+                .overrideCurrentUserForTest(PERSONAL_USER)
+                .setRootListForAllUsers(rootListAllUsers);
+        assertThat(mCombiner.createPresentableListForAllUsers(mUserIds, mUserIdToLabelMap))
+                .comparingElementsUsing(ITEM_CORRESPONDENCE)
+                .containsExactlyElementsIn(WORK_ITEMS)
+                .inOrder();
+    }
+
+    @Test
+    public void testCreatePresentableListForAllUsers_currentIsWork_workItemsOnly() {
+        final List<List<Item>> rootListAllUsers = new ArrayList<>();
+        rootListAllUsers.add(Collections.emptyList());
+        rootListAllUsers.add(Lists.newArrayList(WORK_ITEMS));
+        if (SdkLevel.isAtLeastV()) {
+            rootListAllUsers.add(Collections.emptyList());
+        }
+        mCombiner = new UserItemsCombiner(mResources, mDpm, mState)
+                .overrideCurrentUserForTest(WORK_USER)
+                .setRootListForAllUsers(rootListAllUsers);
+        assertThat(mCombiner.createPresentableListForAllUsers(mUserIds, mUserIdToLabelMap))
+                .comparingElementsUsing(ITEM_CORRESPONDENCE)
+                .containsExactlyElementsIn(WORK_ITEMS)
+                .inOrder();
+    }
+
+    @Test
+    public void testCreatePresentableListForAllUsers_currentIsPrivate_workItemsOnly() {
+        if (!SdkLevel.isAtLeastV()) return;
+        final List<List<Item>> rootListAllUsers = new ArrayList<>();
+        rootListAllUsers.add(Collections.emptyList());
+        rootListAllUsers.add(Lists.newArrayList(WORK_ITEMS));
+        rootListAllUsers.add(Collections.emptyList());
+        mCombiner = new UserItemsCombiner(mResources, mDpm, mState)
+                .overrideCurrentUserForTest(PRIVATE_USER)
+                .setRootListForAllUsers(rootListAllUsers);
+        assertThat(mCombiner.createPresentableListForAllUsers(mUserIds, mUserIdToLabelMap))
+                .comparingElementsUsing(ITEM_CORRESPONDENCE)
+                .containsExactlyElementsIn(WORK_ITEMS)
+                .inOrder();
+    }
+
+    @Test
     public void testCreatePresentableList_currentIsPersonal_personalAndWorkItems() {
         mCombiner = new UserItemsCombiner(mResources, mDpm, mState)
                 .setRootListForCurrentUser(PERSONAL_ITEMS)
@@ -173,6 +309,89 @@
     }
 
     @Test
+    public void testCreatePresentableListForAllUsers_currentIsPersonal_allUsersItems() {
+        final List<List<Item>> rootListAllUsers = new ArrayList<>();
+        rootListAllUsers.add(PERSONAL_ITEMS);
+        rootListAllUsers.add(Lists.newArrayList(WORK_ITEMS));
+        if (SdkLevel.isAtLeastV()) {
+            rootListAllUsers.add(PRIVATE_ITEMS);
+        }
+        mCombiner = new UserItemsCombiner(mResources, mDpm, mState)
+                .setRootListForAllUsers(rootListAllUsers);
+
+        List<Item> expected = Lists.newArrayList();
+        expected.add(new HeaderItem("Personal"));
+        expected.addAll(PERSONAL_ITEMS);
+        expected.add(new HeaderItem("Work"));
+        expected.addAll(WORK_ITEMS);
+        if (SdkLevel.isAtLeastV()) {
+            expected.add(new HeaderItem("Private"));
+            expected.addAll(PRIVATE_ITEMS);
+        }
+
+        assertThat(mCombiner.createPresentableListForAllUsers(mUserIds, mUserIdToLabelMap))
+                .comparingElementsUsing(ITEM_CORRESPONDENCE)
+                .containsExactlyElementsIn(expected)
+                .inOrder();
+    }
+
+    @Test
+    public void testCreatePresentableListForAllUsers_currentIsWork_allUsersItems() {
+        final List<List<Item>> rootListAllUsers = new ArrayList<>();
+        rootListAllUsers.add(PERSONAL_ITEMS);
+        rootListAllUsers.add(Lists.newArrayList(WORK_ITEMS));
+        if (SdkLevel.isAtLeastV()) {
+            rootListAllUsers.add(PRIVATE_ITEMS);
+        }
+        mCombiner = new UserItemsCombiner(mResources, mDpm, mState)
+                .overrideCurrentUserForTest(WORK_USER)
+                .setRootListForAllUsers(rootListAllUsers);
+
+        List<Item> expected = Lists.newArrayList();
+        expected.add(new HeaderItem("Personal"));
+        expected.addAll(PERSONAL_ITEMS);
+        expected.add(new HeaderItem("Work"));
+        expected.addAll(WORK_ITEMS);
+        if (SdkLevel.isAtLeastV()) {
+            expected.add(new HeaderItem("Private"));
+            expected.addAll(PRIVATE_ITEMS);
+        }
+
+        assertThat(mCombiner.createPresentableListForAllUsers(mUserIds, mUserIdToLabelMap))
+                .comparingElementsUsing(ITEM_CORRESPONDENCE)
+                .containsExactlyElementsIn(expected)
+                .inOrder();
+    }
+
+    @Test
+    public void testCreatePresentableListForAllUsers_currentIsPrivate_allUsersItems() {
+        final List<List<Item>> rootListAllUsers = new ArrayList<>();
+        rootListAllUsers.add(PERSONAL_ITEMS);
+        rootListAllUsers.add(Lists.newArrayList(WORK_ITEMS));
+        if (SdkLevel.isAtLeastV()) {
+            rootListAllUsers.add(PRIVATE_ITEMS);
+        }
+        mCombiner = new UserItemsCombiner(mResources, mDpm, mState)
+                .overrideCurrentUserForTest(PRIVATE_USER)
+                .setRootListForAllUsers(rootListAllUsers);
+
+        List<Item> expected = Lists.newArrayList();
+        expected.add(new HeaderItem("Personal"));
+        expected.addAll(PERSONAL_ITEMS);
+        expected.add(new HeaderItem("Work"));
+        expected.addAll(WORK_ITEMS);
+        if (SdkLevel.isAtLeastV()) {
+            expected.add(new HeaderItem("Private"));
+            expected.addAll(PRIVATE_ITEMS);
+        }
+
+        assertThat(mCombiner.createPresentableListForAllUsers(mUserIds, mUserIdToLabelMap))
+                .comparingElementsUsing(ITEM_CORRESPONDENCE)
+                .containsExactlyElementsIn(expected)
+                .inOrder();
+    }
+
+    @Test
     public void testCreatePresentableList_currentIsPersonal_personalAndWorkItems_cannotShare() {
         mState.canShareAcrossProfile = false;
         mCombiner = new UserItemsCombiner(mResources, mDpm, mState)
@@ -196,6 +415,56 @@
         assertThat(mCombiner.createPresentableList()).isEmpty();
     }
 
+    @Test
+    public void testCreatePresentableListForAllUsers_currentIsPersonal_cannotShareToWork() {
+        if (!FeatureFlagUtils.isPrivateSpaceEnabled()) return;
+        mState.canForwardToProfileIdMap.put(WORK_USER, false);
+        final List<List<Item>> rootListAllUsers = new ArrayList<>();
+        rootListAllUsers.add(PERSONAL_ITEMS);
+        rootListAllUsers.add(Lists.newArrayList(WORK_ITEMS));
+        if (SdkLevel.isAtLeastV()) {
+            rootListAllUsers.add(PRIVATE_ITEMS);
+        }
+        mCombiner = new UserItemsCombiner(mResources, mDpm, mState)
+                .setRootListForAllUsers(rootListAllUsers);
+
+        List<Item> expected = Lists.newArrayList();
+        expected.add(new HeaderItem("Personal"));
+        expected.addAll(PERSONAL_ITEMS);
+        if (SdkLevel.isAtLeastV()) {
+            expected.add(new HeaderItem("Private"));
+            expected.addAll(PRIVATE_ITEMS);
+        }
+
+        assertThat(mCombiner.createPresentableListForAllUsers(mUserIds, mUserIdToLabelMap))
+                .comparingElementsUsing(ITEM_CORRESPONDENCE)
+                .containsExactlyElementsIn(expected)
+                .inOrder();
+    }
+
+    @Test
+    public void testCreatePresentableListForAllUsers_currentIsWork_AllItems_cannotSharePersonal() {
+        if (!FeatureFlagUtils.isPrivateSpaceEnabled()) return;
+        mState.canForwardToProfileIdMap.put(PERSONAL_USER, false);
+        // In the current implementation of cross profile content sharing strategy work profile will
+        // be able to share to all the child profiles of the parent/personal profile only if it is
+        // able to share with parent/personal profile
+        mState.canForwardToProfileIdMap.put(PRIVATE_USER, false);
+        final List<List<Item>> rootListAllUsers = new ArrayList<>();
+        rootListAllUsers.add(PERSONAL_ITEMS);
+        rootListAllUsers.add(Lists.newArrayList(WORK_ITEMS));
+        if (SdkLevel.isAtLeastV()) {
+            rootListAllUsers.add(PRIVATE_ITEMS);
+        }
+        mCombiner = new UserItemsCombiner(mResources, mDpm, mState)
+                .setRootListForAllUsers(rootListAllUsers);
+
+        assertThat(mCombiner.createPresentableListForAllUsers(mUserIds, mUserIdToLabelMap))
+                .comparingElementsUsing(ITEM_CORRESPONDENCE)
+                .containsExactlyElementsIn(WORK_ITEMS)
+                .inOrder();
+    }
+
     private static TestItem personalItem(String title) {
         return new TestItem(title, PERSONAL_USER);
     }
@@ -204,6 +473,10 @@
         return new TestItem(title, WORK_USER);
     }
 
+    private static TestItem privateItem(String title) {
+        return new TestItem(title, PRIVATE_USER);
+    }
+
     private static class TestItem extends Item {
 
         TestItem(String title, UserId userId) {