/*
 * 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 com.android.documentsui;

import static com.android.documentsui.DevicePolicyResources.Drawables.Style.SOLID_COLORED;
import static com.android.documentsui.DevicePolicyResources.Drawables.WORK_PROFILE_ICON;
import static com.android.documentsui.DevicePolicyResources.Strings.PERSONAL_TAB;
import static com.android.documentsui.DevicePolicyResources.Strings.WORK_TAB;

import static com.google.common.truth.Truth.assertWithMessage;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import android.Manifest;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyResourcesManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.UserProperties;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.os.UserHandle;
import android.os.UserManager;

import androidx.test.filters.SdkSuppress;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;

import com.android.documentsui.base.UserId;
import com.android.documentsui.testing.UserManagers;
import com.android.documentsui.util.VersionUtils;
import com.android.modules.utils.build.SdkLevel;

import com.google.common.collect.Lists;

import org.junit.Before;
import org.junit.Test;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@SmallTest
@SdkSuppress(minSdkVersion = 31, codeName = "S")
public class UserManagerStateTest {

    private static final String PERSONAL = "Personal";
    private static final String WORK = "Work";
    private static final String PRIVATE = "Private";

    private final UserHandle mSystemUser = UserHandle.SYSTEM;
    private final UserHandle mManagedUser = UserHandle.of(100);
    private final UserHandle mPrivateUser = UserHandle.of(101);
    private final UserHandle mOtherUser = UserHandle.of(102);
    private final UserHandle mNormalUser = UserHandle.of(103);

    private final ResolveInfo mMockInfo1 = mock(ResolveInfo.class);
    private final ResolveInfo mMockInfo2 = mock(ResolveInfo.class);
    private final ResolveInfo mMockInfo3 = mock(ResolveInfo.class);

    private final Context mMockContext = mock(Context.class);
    private final Intent mMockIntent = mock(Intent.class);
    private final UserManager mMockUserManager = UserManagers.create();
    private final PackageManager mMockPackageManager = mock(PackageManager.class);
    private final DevicePolicyManager mDevicePolicyManager = mock(DevicePolicyManager.class);
    private UserManagerState mUserManagerState;

    @Before
    public void setup() throws Exception {
        when(mMockContext.getApplicationContext()).thenReturn(mMockContext);

        when(mMockUserManager.isManagedProfile(mManagedUser.getIdentifier())).thenReturn(true);
        when(mMockUserManager.isManagedProfile(mSystemUser.getIdentifier())).thenReturn(false);
        when(mMockUserManager.isManagedProfile(mPrivateUser.getIdentifier())).thenReturn(false);
        when(mMockUserManager.isManagedProfile(mOtherUser.getIdentifier())).thenReturn(false);

        if (SdkLevel.isAtLeastV()) {
            UserProperties systemUserProperties = new UserProperties.Builder()
                    .setShowInSharingSurfaces(UserProperties.SHOW_IN_SHARING_SURFACES_SEPARATE)
                    .setCrossProfileContentSharingStrategy(
                            UserProperties.CROSS_PROFILE_CONTENT_SHARING_NO_DELEGATION)
                    .build();
            UserProperties managedUserProperties = new UserProperties.Builder()
                    .setShowInSharingSurfaces(UserProperties.SHOW_IN_SHARING_SURFACES_SEPARATE)
                    .setCrossProfileContentSharingStrategy(
                            UserProperties.CROSS_PROFILE_CONTENT_SHARING_NO_DELEGATION)
                    .setShowInQuietMode(UserProperties.SHOW_IN_QUIET_MODE_PAUSED)
                    .build();
            UserProperties privateUserProperties = new UserProperties.Builder()
                    .setShowInSharingSurfaces(UserProperties.SHOW_IN_SHARING_SURFACES_SEPARATE)
                    .setCrossProfileContentSharingStrategy(
                            UserProperties.CROSS_PROFILE_CONTENT_SHARING_DELEGATE_FROM_PARENT)
                    .setShowInQuietMode(UserProperties.SHOW_IN_QUIET_MODE_HIDDEN)
                    .build();
            UserProperties otherUserProperties = new UserProperties.Builder()
                    .setShowInSharingSurfaces(UserProperties.SHOW_IN_SHARING_SURFACES_WITH_PARENT)
                    .setCrossProfileContentSharingStrategy(
                            UserProperties.CROSS_PROFILE_CONTENT_SHARING_DELEGATE_FROM_PARENT)
                    .build();
            UserProperties normalUserProperties = new UserProperties.Builder()
                    .setShowInSharingSurfaces(UserProperties.SHOW_IN_SHARING_SURFACES_NO)
                    .setCrossProfileContentSharingStrategy(
                            UserProperties.CROSS_PROFILE_CONTENT_SHARING_DELEGATE_FROM_PARENT)
                    .build();
            when(mMockUserManager.getUserProperties(mSystemUser)).thenReturn(systemUserProperties);
            when(mMockUserManager.getUserProperties(mManagedUser)).thenReturn(
                    managedUserProperties);
            when(mMockUserManager.getUserProperties(mPrivateUser)).thenReturn(
                    privateUserProperties);
            when(mMockUserManager.getUserProperties(mOtherUser)).thenReturn(otherUserProperties);
            when(mMockUserManager.getUserProperties(mNormalUser)).thenReturn(normalUserProperties);
        }

        when(mMockUserManager.getProfileParent(mSystemUser)).thenReturn(null);
        when(mMockUserManager.getProfileParent(mManagedUser)).thenReturn(mSystemUser);
        when(mMockUserManager.getProfileParent(mPrivateUser)).thenReturn(mSystemUser);
        when(mMockUserManager.getProfileParent(mOtherUser)).thenReturn(mSystemUser);
        when(mMockUserManager.getProfileParent(mNormalUser)).thenReturn(null);

        if (SdkLevel.isAtLeastR()) {
            when(mMockInfo1.isCrossProfileIntentForwarderActivity()).thenReturn(true);
            when(mMockInfo2.isCrossProfileIntentForwarderActivity()).thenReturn(false);
            when(mMockInfo3.isCrossProfileIntentForwarderActivity()).thenReturn(false);
        }

        when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
        when(mMockContext.getSystemServiceName(UserManager.class)).thenReturn("mMockUserManager");
        when(mMockContext.getSystemService(UserManager.class)).thenReturn(mMockUserManager);
        when(mMockContext.getSystemServiceName(DevicePolicyManager.class))
                .thenReturn(Context.DEVICE_POLICY_SERVICE);
        when(mMockContext.getSystemService(Context.DEVICE_POLICY_SERVICE))
                .thenReturn(mDevicePolicyManager);
        when(mMockContext.getResources()).thenReturn(
                InstrumentationRegistry.getInstrumentation().getTargetContext().getResources());
    }

    @Test
    public void testGetUserIds_onlySystemUser_returnsSystemUser() {
        UserId currentUser = UserId.of(mSystemUser);
        initializeUserManagerState(currentUser, Lists.newArrayList(mSystemUser));

        assertWithMessage("getUserIds returns unexpected list of user ids")
                .that(mUserManagerState.getUserIds()).containsExactly(UserId.of(mSystemUser));
    }

    @Test
    public void testGetUserIds_allProfilesCurrentUserSystem_allShowInSharingSurfacesSeparate() {
        if (!SdkLevel.isAtLeastV()) return;
        UserId currentUser = UserId.of(mSystemUser);
        initializeUserManagerState(currentUser,
                Lists.newArrayList(mSystemUser, mManagedUser, mPrivateUser, mOtherUser,
                        mNormalUser));

        assertWithMessage("getUserIds returns unexpected list of user ids")
                .that(mUserManagerState.getUserIds())
                .containsExactly(UserId.of(mSystemUser), UserId.of(mManagedUser),
                        UserId.of(mPrivateUser));
    }

    @Test
    public void testGetUserIds_allProfilesCurrentUserManaged_allShowInSharingSurfacesSeparate() {
        if (!SdkLevel.isAtLeastV()) return;
        UserId currentUser = UserId.of(mManagedUser);
        initializeUserManagerState(currentUser,
                Lists.newArrayList(mSystemUser, mManagedUser, mPrivateUser, mOtherUser,
                        mNormalUser));

        assertWithMessage("getUserIds returns unexpected list of user ids")
                .that(mUserManagerState.getUserIds())
                .containsExactly(UserId.of(mSystemUser), UserId.of(mManagedUser),
                        UserId.of(mPrivateUser));
    }

    @Test
    public void testGetUserIds_allProfilesCurrentUserPrivate_allShowInSharingSurfacesSeparate() {
        if (!SdkLevel.isAtLeastV()) return;
        UserId currentUser = UserId.of(mPrivateUser);
        initializeUserManagerState(currentUser,
                Lists.newArrayList(mSystemUser, mManagedUser, mPrivateUser, mOtherUser));

        assertWithMessage("getUserIds returns unexpected list of user ids")
                .that(mUserManagerState.getUserIds())
                .containsExactly(UserId.of(mSystemUser), UserId.of(mManagedUser),
                        UserId.of(mPrivateUser));
    }

    @Test
    public void testGetUserIds_systemAndManagedUserCurrentUserSystem_returnsBoth() {
        UserId currentUser = UserId.of(mSystemUser);
        initializeUserManagerState(currentUser, Lists.newArrayList(mSystemUser, mManagedUser));

        assertWithMessage("getUserIds returns unexpected list of user ids")
                .that(mUserManagerState.getUserIds())
                .containsExactly(UserId.of(mSystemUser), UserId.of(mManagedUser));
    }

    @Test
    public void testGetUserIds_systemAndManagedUserCurrentUserManaged_returnsBoth() {
        UserId currentUser = UserId.of(mManagedUser);
        initializeUserManagerState(currentUser, Lists.newArrayList(mSystemUser, mManagedUser));

        assertWithMessage("getUserIds returns unexpected list of user ids")
                .that(mUserManagerState.getUserIds())
                .containsExactly(UserId.of(mSystemUser), UserId.of(mManagedUser));
    }

    @Test
    public void testGetUserIds_systemAndPrivateUserCurrentUserSystem_returnsBoth() {
        if (!SdkLevel.isAtLeastV()) return;
        UserId currentUser = UserId.of(mSystemUser);
        initializeUserManagerState(currentUser, Lists.newArrayList(mSystemUser, mPrivateUser));

        assertWithMessage("getUserIds returns unexpected list of user ids")
                .that(mUserManagerState.getUserIds())
                .containsExactly(UserId.of(mSystemUser), UserId.of(mPrivateUser));
    }

    @Test
    public void testGetUserIds_systemAndPrivateUserCurrentUserPrivate_returnsBoth() {
        if (!SdkLevel.isAtLeastV()) return;
        UserId currentUser = UserId.of(mPrivateUser);
        initializeUserManagerState(currentUser, Lists.newArrayList(mSystemUser, mPrivateUser));

        assertWithMessage("getUserIds returns unexpected list of user ids")
                .that(mUserManagerState.getUserIds())
                .containsExactly(UserId.of(mSystemUser), UserId.of(mPrivateUser));
    }

    @Test
    public void testGetUserIds_systemAndOtherUserCurrentUserOtherPreV_returnsCurrentUser() {
        if (SdkLevel.isAtLeastV()) return;
        UserId currentUser = UserId.of(mOtherUser);
        initializeUserManagerState(currentUser, Lists.newArrayList(mSystemUser, mOtherUser));

        assertWithMessage("getUserIds returns unexpected list of user ids")
                .that(mUserManagerState.getUserIds())
                .containsExactly(currentUser);
    }

    @Test
    public void testGetUserIds_systemAndOtherUserCurrentUserOtherPostV_returnsSystemUser() {
        if (!SdkLevel.isAtLeastV()) return;
        UserId currentUser = UserId.of(mOtherUser);
        initializeUserManagerState(currentUser, Lists.newArrayList(mSystemUser, mOtherUser));

        assertWithMessage("getUserIds returns unexpected list of user ids")
                .that(mUserManagerState.getUserIds())
                .containsExactly(UserId.of(mSystemUser));
    }

    @Test
    public void testGetUserIds_normalAndOtherUserCurrentUserNormal_returnsCurrentUser() {
        // since both users do not have show in sharing surfaces separate, returns current user
        UserId currentUser = UserId.of(mNormalUser);
        initializeUserManagerState(currentUser, Lists.newArrayList(mOtherUser, mNormalUser));

        assertWithMessage("getUserIds returns unexpected list of user ids")
                .that(mUserManagerState.getUserIds())
                .containsExactly(UserId.of(mNormalUser));
    }

    @Test
    public void testGetUserIds_systemAndManagedUserCurrentUserSystem_returnsBothInOrder() {
        // Returns the both if there are system and managed users.
        if (SdkLevel.isAtLeastV()) return;
        UserId currentUser = UserId.of(mSystemUser);
        initializeUserManagerState(currentUser, Lists.newArrayList(mSystemUser, mManagedUser));
        assertWithMessage("getUserIds returns unexpected list of user ids")
                .that(mUserManagerState.getUserIds())
                .containsExactly(UserId.of(mSystemUser), UserId.of(mManagedUser)).inOrder();
    }

    @Test
    public void testGetUserIds_systemAndManagedUserCurrentUserManaged_returnsBothInOrder() {
        // Returns the both if there are system and managed users.
        if (SdkLevel.isAtLeastV()) return;
        UserId currentUser = UserId.of(mManagedUser);
        initializeUserManagerState(currentUser, Lists.newArrayList(mSystemUser, mManagedUser));
        assertWithMessage("getUserIds returns unexpected list of user ids")
                .that(mUserManagerState.getUserIds())
                .containsExactly(UserId.of(mSystemUser), UserId.of(mManagedUser)).inOrder();
    }

    @Test
    public void testGetUserIds_managedAndSystemUserCurrentUserSystem_returnsBothInOrder() {
        // Returns the both if there are system and managed users, regardless of input list order.
        if (SdkLevel.isAtLeastV()) return;
        UserId currentUser = UserId.of(mSystemUser);
        initializeUserManagerState(currentUser, Lists.newArrayList(mManagedUser, mSystemUser));
        assertWithMessage("getUserIds returns unexpected list of user ids")
                .that(mUserManagerState.getUserIds())
                .containsExactly(UserId.of(mSystemUser), UserId.of(mManagedUser)).inOrder();
    }

    @Test
    public void testGetUserIds_otherAndManagedUserCurrentUserOtherPreV_returnsCurrentUser() {
        // When there is no system user, returns the current user.
        // This is a case theoretically can happen but we don't expect. So we return the current
        // user only.
        if (SdkLevel.isAtLeastV()) return;
        UserId currentUser = UserId.of(mOtherUser);
        initializeUserManagerState(currentUser, Lists.newArrayList(mOtherUser, mManagedUser));
        assertWithMessage("getUserIds returns unexpected list of user ids")
                .that(mUserManagerState.getUserIds()).containsExactly(currentUser);
    }

    @Test
    public void testGetUserIds_otherAndManagedUserCurrentUserOtherPostV_returnsManagedUser() {
        // Only the users with show in sharing surfaces separate are eligible to be returned
        if (!SdkLevel.isAtLeastV()) return;
        UserId currentUser = UserId.of(mOtherUser);
        initializeUserManagerState(currentUser, Lists.newArrayList(mOtherUser, mManagedUser));
        assertWithMessage("getUserIds returns unexpected list of user ids")
                .that(mUserManagerState.getUserIds()).containsExactly(UserId.of(mManagedUser));
    }

    @Test
    public void testGetUserIds_otherAndManagedUserCurrentUserManaged_returnsCurrentUser() {
        // When there is no system user, returns the current user.
        // This is a case theoretically can happen, but we don't expect. So we return the current
        // user only.
        UserId currentUser = UserId.of(mManagedUser);
        initializeUserManagerState(currentUser, Lists.newArrayList(mOtherUser, mManagedUser));
        assertWithMessage("getUserIds returns unexpected list of user ids")
                .that(mUserManagerState.getUserIds()).containsExactly(currentUser);
    }

    @Test
    public void testGetUserIds_unsupportedDeviceCurrent_returnsCurrentUser() {
        // This test only tests for Android R or later. This test case always passes before R.
        if (VersionUtils.isAtLeastR()) {
            // When permission is denied, only returns the current user.
            when(mMockContext.checkSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS))
                    .thenReturn(PackageManager.PERMISSION_DENIED);
            UserId currentUser = UserId.of(mSystemUser);
            when(mMockUserManager.getUserProfiles()).thenReturn(
                    Lists.newArrayList(mSystemUser, mManagedUser, mPrivateUser, mOtherUser));
            mUserManagerState = UserManagerState.create(mMockContext);
            assertWithMessage("Unsupported device should have returned only the current user")
                    .that(mUserManagerState.getUserIds()).containsExactly(currentUser);
        }
    }

    @Test
    public void testGetUserIds_returnCachedList() {
        // Returns all three if there are system, managed and private users.
        UserId currentUser = UserId.of(mSystemUser);
        initializeUserManagerState(currentUser,
                Lists.newArrayList(mSystemUser, mManagedUser, mPrivateUser, mOtherUser));
        assertWithMessage("getUserIds does not return cached instance")
                .that(mUserManagerState.getUserIds())
                .isSameInstanceAs(mUserManagerState.getUserIds());
    }

    @Test
    public void testGetCanForwardToProfileIdMap_systemUserCanForwardToAll() {
        UserId currentUser = UserId.of(mSystemUser);
        final List<ResolveInfo> mMockResolveInfoList = Lists.newArrayList(mMockInfo1, mMockInfo2);
        if (SdkLevel.isAtLeastV()) {
            initializeUserManagerState(currentUser,
                    Lists.newArrayList(mSystemUser, mManagedUser, mPrivateUser));
            when(mMockPackageManager.queryIntentActivitiesAsUser(mMockIntent,
                    PackageManager.MATCH_DEFAULT_ONLY, mSystemUser)).thenReturn(
                    mMockResolveInfoList);
        } else {
            initializeUserManagerState(currentUser,
                    Lists.newArrayList(mSystemUser, mManagedUser));
            when(mMockPackageManager.queryIntentActivities(mMockIntent,
                    PackageManager.MATCH_DEFAULT_ONLY)).thenReturn(mMockResolveInfoList);
        }

        Map<UserId, Boolean> expectedCanForwardToProfileIdMap = new HashMap<>();
        expectedCanForwardToProfileIdMap.put(UserId.of(mSystemUser), true);
        expectedCanForwardToProfileIdMap.put(UserId.of(mManagedUser), true);
        if (SdkLevel.isAtLeastV()) {
            expectedCanForwardToProfileIdMap.put(UserId.of(mPrivateUser), true);
        }

        assertWithMessage("getCanForwardToProfileIdMap returns incorrect mappings")
                .that(mUserManagerState.getCanForwardToProfileIdMap(mMockIntent))
                .isEqualTo(expectedCanForwardToProfileIdMap);
    }

    @Test
    public void testGetCanForwardToProfileIdMap_systemUserCanForwardToManaged() {
        UserId currentUser = UserId.of(mSystemUser);
        initializeUserManagerState(currentUser, Lists.newArrayList(mSystemUser, mManagedUser));
        final List<ResolveInfo> mMockResolveInfoList = Lists.newArrayList(mMockInfo1, mMockInfo2);
        if (SdkLevel.isAtLeastV()) {
            when(mMockPackageManager.queryIntentActivitiesAsUser(mMockIntent,
                    PackageManager.MATCH_DEFAULT_ONLY, mSystemUser)).thenReturn(
                    mMockResolveInfoList);
        } else {
            when(mMockPackageManager.queryIntentActivities(mMockIntent,
                    PackageManager.MATCH_DEFAULT_ONLY)).thenReturn(mMockResolveInfoList);
        }

        Map<UserId, Boolean> expectedCanForwardToProfileIdMap = new HashMap<>();
        expectedCanForwardToProfileIdMap.put(UserId.of(mSystemUser), true);
        expectedCanForwardToProfileIdMap.put(UserId.of(mManagedUser), true);

        assertWithMessage("getCanForwardToProfileIdMap returns incorrect mappings")
                .that(mUserManagerState.getCanForwardToProfileIdMap(mMockIntent))
                .isEqualTo(expectedCanForwardToProfileIdMap);
    }

    @Test
    public void testGetCanForwardToProfileIdMap_systemUserCanAlwaysForwardToPrivate() {
        if (!SdkLevel.isAtLeastV()) return;
        UserId currentUser = UserId.of(mSystemUser);
        initializeUserManagerState(currentUser, Lists.newArrayList(mSystemUser, mPrivateUser));

        Map<UserId, Boolean> expectedCanForwardToProfileIdMap = new HashMap<>();
        expectedCanForwardToProfileIdMap.put(UserId.of(mSystemUser), true);
        expectedCanForwardToProfileIdMap.put(UserId.of(mPrivateUser), true);

        assertWithMessage("getCanForwardToProfileIdMap returns incorrect mappings")
                .that(mUserManagerState.getCanForwardToProfileIdMap(mMockIntent))
                .isEqualTo(expectedCanForwardToProfileIdMap);
    }

    @Test
    public void testGetCanForwardToProfileIdMap_systemUserCanNotForwardToManagedUser() {
        UserId currentUser = UserId.of(mSystemUser);
        final List<ResolveInfo> mMockResolveInfoList = Lists.newArrayList(mMockInfo2, mMockInfo3);
        if (SdkLevel.isAtLeastV()) {
            initializeUserManagerState(currentUser,
                    Lists.newArrayList(mSystemUser, mManagedUser, mPrivateUser));
            when(mMockPackageManager.queryIntentActivitiesAsUser(mMockIntent,
                    PackageManager.MATCH_DEFAULT_ONLY, mSystemUser)).thenReturn(
                    mMockResolveInfoList);
        } else {
            initializeUserManagerState(currentUser,
                    Lists.newArrayList(mSystemUser, mManagedUser));
            when(mMockPackageManager.queryIntentActivities(mMockIntent,
                    PackageManager.MATCH_DEFAULT_ONLY)).thenReturn(mMockResolveInfoList);
        }

        Map<UserId, Boolean> expectedCanForwardToProfileIdMap = new HashMap<>();
        expectedCanForwardToProfileIdMap.put(UserId.of(mSystemUser), true);
        expectedCanForwardToProfileIdMap.put(UserId.of(mManagedUser), false);
        if (SdkLevel.isAtLeastV()) {
            expectedCanForwardToProfileIdMap.put(UserId.of(mPrivateUser), true);
        }

        assertWithMessage("getCanForwardToProfileIdMap returns incorrect mappings")
                .that(mUserManagerState.getCanForwardToProfileIdMap(mMockIntent))
                .isEqualTo(expectedCanForwardToProfileIdMap);
    }

    @Test
    public void testGetCanForwardToProfileIdMap_managedCanForwardToAll() {
        UserId currentUser = UserId.of(mManagedUser);
        final List<ResolveInfo> mMockResolveInfoList = Lists.newArrayList(mMockInfo1, mMockInfo2);
        if (SdkLevel.isAtLeastV()) {
            initializeUserManagerState(currentUser,
                    Lists.newArrayList(mSystemUser, mManagedUser, mPrivateUser));
            when(mMockPackageManager.queryIntentActivitiesAsUser(mMockIntent,
                    PackageManager.MATCH_DEFAULT_ONLY, mManagedUser)).thenReturn(
                    mMockResolveInfoList);
        } else {
            initializeUserManagerState(currentUser,
                    Lists.newArrayList(mSystemUser, mManagedUser));
            when(mMockPackageManager.queryIntentActivities(mMockIntent,
                    PackageManager.MATCH_DEFAULT_ONLY)).thenReturn(mMockResolveInfoList);
        }

        Map<UserId, Boolean> expectedCanForwardToProfileIdMap = new HashMap<>();
        expectedCanForwardToProfileIdMap.put(UserId.of(mSystemUser), true);
        expectedCanForwardToProfileIdMap.put(UserId.of(mManagedUser), true);
        if (SdkLevel.isAtLeastV()) {
            expectedCanForwardToProfileIdMap.put(UserId.of(mPrivateUser), true);
        }

        assertWithMessage("getCanForwardToProfileIdMap returns incorrect mappings")
                .that(mUserManagerState.getCanForwardToProfileIdMap(mMockIntent))
                .isEqualTo(expectedCanForwardToProfileIdMap);
    }

    @Test
    public void testGetCanForwardToProfileIdMap_managedCanNotForwardToAll() {
        UserId currentUser = UserId.of(mManagedUser);
        final List<ResolveInfo> mMockResolveInfoList = Lists.newArrayList(mMockInfo2, mMockInfo3);

        if (SdkLevel.isAtLeastV()) {
            initializeUserManagerState(currentUser,
                    Lists.newArrayList(mSystemUser, mManagedUser, mPrivateUser));
            when(mMockPackageManager.queryIntentActivitiesAsUser(mMockIntent,
                    PackageManager.MATCH_DEFAULT_ONLY, mSystemUser)).thenReturn(
                    mMockResolveInfoList);
        } else {
            initializeUserManagerState(currentUser,
                    Lists.newArrayList(mSystemUser, mManagedUser));
            when(mMockPackageManager.queryIntentActivities(mMockIntent,
                    PackageManager.MATCH_DEFAULT_ONLY)).thenReturn(mMockResolveInfoList);
        }

        Map<UserId, Boolean> expectedCanForwardToProfileIdMap = new HashMap<>();
        expectedCanForwardToProfileIdMap.put(UserId.of(mSystemUser), false);
        expectedCanForwardToProfileIdMap.put(UserId.of(mManagedUser), true);
        if (SdkLevel.isAtLeastV()) {
            expectedCanForwardToProfileIdMap.put(UserId.of(mPrivateUser), false);
        }

        assertWithMessage("getCanForwardToProfileIdMap returns incorrect mappings")
                .that(mUserManagerState.getCanForwardToProfileIdMap(mMockIntent))
                .isEqualTo(expectedCanForwardToProfileIdMap);
    }

    @Test
    public void testGetCanForwardToProfileIdMap_privateCanForwardToAll() {
        if (!SdkLevel.isAtLeastV()) return;
        UserId currentUser = UserId.of(mPrivateUser);
        initializeUserManagerState(currentUser,
                Lists.newArrayList(mSystemUser, mManagedUser, mPrivateUser));
        final List<ResolveInfo> mMockResolveInfoList = Lists.newArrayList(mMockInfo1, mMockInfo2);
        when(mMockPackageManager.queryIntentActivitiesAsUser(mMockIntent,
                PackageManager.MATCH_DEFAULT_ONLY, mSystemUser)).thenReturn(mMockResolveInfoList);

        Map<UserId, Boolean> expectedCanForwardToProfileIdMap = new HashMap<>();
        expectedCanForwardToProfileIdMap.put(UserId.of(mSystemUser), true);
        expectedCanForwardToProfileIdMap.put(UserId.of(mManagedUser), true);
        expectedCanForwardToProfileIdMap.put(UserId.of(mPrivateUser), true);

        assertWithMessage("getCanForwardToProfileIdMap returns incorrect mappings")
                .that(mUserManagerState.getCanForwardToProfileIdMap(mMockIntent))
                .isEqualTo(expectedCanForwardToProfileIdMap);
    }

    @Test
    public void testGetCanForwardToProfileIdMap_privateCanNotForwardToManagedUser() {
        if (!SdkLevel.isAtLeastV()) return;
        UserId currentUser = UserId.of(mPrivateUser);
        initializeUserManagerState(currentUser,
                Lists.newArrayList(mSystemUser, mManagedUser, mPrivateUser));
        final List<ResolveInfo> mMockResolveInfoList = Lists.newArrayList(mMockInfo2, mMockInfo3);
        when(mMockPackageManager.queryIntentActivitiesAsUser(mMockIntent,
                PackageManager.MATCH_DEFAULT_ONLY, mSystemUser)).thenReturn(mMockResolveInfoList);

        Map<UserId, Boolean> expectedCanForwardToProfileIdMap = new HashMap<>();
        expectedCanForwardToProfileIdMap.put(UserId.of(mSystemUser), true);
        expectedCanForwardToProfileIdMap.put(UserId.of(mManagedUser), false);
        expectedCanForwardToProfileIdMap.put(UserId.of(mPrivateUser), true);

        assertWithMessage("getCanForwardToProfileIdMap returns incorrect mappings")
                .that(mUserManagerState.getCanForwardToProfileIdMap(mMockIntent))
                .isEqualTo(expectedCanForwardToProfileIdMap);
    }

    @Test
    public void testGetCanForwardToProfileIdMap_privateCanAlwaysForwardToSystemUser() {
        if (!SdkLevel.isAtLeastV()) return;
        UserId currentUser = UserId.of(mPrivateUser);
        initializeUserManagerState(currentUser, Lists.newArrayList(mSystemUser, mPrivateUser));

        Map<UserId, Boolean> expectedCanForwardToProfileIdMap = new HashMap<>();
        expectedCanForwardToProfileIdMap.put(UserId.of(mSystemUser), true);
        expectedCanForwardToProfileIdMap.put(UserId.of(mPrivateUser), true);

        assertWithMessage("getCanForwardToProfileIdMap returns incorrect mappings")
                .that(mUserManagerState.getCanForwardToProfileIdMap(mMockIntent))
                .isEqualTo(expectedCanForwardToProfileIdMap);
    }

    @Test
    public void testOnProfileStatusChange_anyIntentActionForManagedProfile() {
        if (!SdkLevel.isAtLeastV()) return;
        UserId currentUser = UserId.of(mSystemUser);
        initializeUserManagerState(currentUser,
                Lists.newArrayList(mSystemUser, mManagedUser, mPrivateUser));

        // UserManagerState#mUserId and UserManagerState#mCanForwardToProfileIdMap will empty
        // by default if the getters of these member variables have not been called
        List<UserId> userIdsBeforeIntent = new ArrayList<>(mUserManagerState.getUserIds());
        Map<UserId, Boolean> canForwardToProfileIdMapBeforeIntent = new HashMap<>(
                mUserManagerState.getCanForwardToProfileIdMap(mMockIntent));

        String action = "any_intent";
        mUserManagerState.onProfileActionStatusChange(action, UserId.of(mManagedUser));

        assertWithMessage("Unexpected changes to user id list on receiving intent: " + action)
                .that(mUserManagerState.getUserIds()).isEqualTo(userIdsBeforeIntent);
        assertWithMessage(
                "Unexpected changes to canForwardToProfileIdMap on receiving intent: " + action)
                .that(mUserManagerState.getCanForwardToProfileIdMap(mMockIntent)).isEqualTo(
                        canForwardToProfileIdMapBeforeIntent);
    }

    @Test
    public void testOnProfileStatusChange_actionProfileUnavailableForPrivateProfile() {
        if (!SdkLevel.isAtLeastV()) return;
        UserId currentUser = UserId.of(mSystemUser);
        UserId managedUser = UserId.of(mManagedUser);
        UserId privateUser = UserId.of(mPrivateUser);
        final List<ResolveInfo> mMockResolveInfoList = Lists.newArrayList(mMockInfo1, mMockInfo2);
        when(mMockPackageManager.queryIntentActivitiesAsUser(mMockIntent,
                PackageManager.MATCH_DEFAULT_ONLY, mSystemUser)).thenReturn(
                mMockResolveInfoList);
        initializeUserManagerState(currentUser,
                Lists.newArrayList(mSystemUser, mManagedUser, mPrivateUser));

        // UserManagerState#mUserId and UserManagerState#mCanForwardToProfileIdMap will empty
        // by default if the getters of these member variables have not been called
        List<UserId> userIdsBeforeIntent = new ArrayList<>(mUserManagerState.getUserIds());
        Map<UserId, Boolean> canForwardToProfileIdMapBeforeIntent = new HashMap<>(
                mUserManagerState.getCanForwardToProfileIdMap(mMockIntent));

        List<UserId> expectedUserIdsAfterIntent = Lists.newArrayList(currentUser, managedUser);

        String action = Intent.ACTION_PROFILE_UNAVAILABLE;
        mUserManagerState.onProfileActionStatusChange(action, privateUser);

        assertWithMessage(
                "UserIds list should not be same before and after receiving intent: " + action)
                .that(mUserManagerState.getUserIds()).isNotEqualTo(userIdsBeforeIntent);
        assertWithMessage("Unexpected changes to user id list on receiving intent: " + action)
                .that(mUserManagerState.getUserIds()).isEqualTo(expectedUserIdsAfterIntent);
        assertWithMessage("CanForwardToLabelMap should be same before and after receiving intent: "
                + action)
                .that(mUserManagerState.getCanForwardToProfileIdMap(mMockIntent)).isEqualTo(
                        canForwardToProfileIdMapBeforeIntent);
    }

    @Test
    public void testOnProfileStatusChange_actionProfileAvailable_profileInitialised() {
        if (!SdkLevel.isAtLeastV()) return;
        UserId currentUser = UserId.of(mSystemUser);
        UserId managedUser = UserId.of(mManagedUser);
        UserId privateUser = UserId.of(mPrivateUser);
        final List<ResolveInfo> mMockResolveInfoList = Lists.newArrayList(mMockInfo1, mMockInfo2);
        when(mMockPackageManager.queryIntentActivitiesAsUser(mMockIntent,
                PackageManager.MATCH_DEFAULT_ONLY, mSystemUser)).thenReturn(
                mMockResolveInfoList);
        initializeUserManagerState(currentUser,
                Lists.newArrayList(mSystemUser, mManagedUser, mPrivateUser));

        // initialising the userIds list and canForwardToProfileIdMap
        mUserManagerState.getUserIds();
        mUserManagerState.getCanForwardToProfileIdMap(mMockIntent);

        // Making the private profile unavailable after it has been initialised
        mUserManagerState.onProfileActionStatusChange(Intent.ACTION_PROFILE_UNAVAILABLE,
                privateUser);

        List<UserId> userIdsBeforeIntent = new ArrayList<>(mUserManagerState.getUserIds());
        Map<UserId, Boolean> canForwardToProfileIdMapBeforeIntent = new HashMap<>(
                mUserManagerState.getCanForwardToProfileIdMap(mMockIntent));

        List<UserId> expectedUserIdsAfterIntent = Lists.newArrayList(currentUser, managedUser,
                privateUser);

        String action = Intent.ACTION_PROFILE_AVAILABLE;
        mUserManagerState.onProfileActionStatusChange(action, privateUser);

        assertWithMessage(
                "UserIds list should not be same before and after receiving intent: " + action)
                .that(mUserManagerState.getUserIds()).isNotEqualTo(userIdsBeforeIntent);
        assertWithMessage("Unexpected changes to user id list on receiving intent: " + action)
                .that(mUserManagerState.getUserIds()).isEqualTo(expectedUserIdsAfterIntent);
        assertWithMessage("CanForwardToLabelMap should be same before and after receiving intent: "
                + action)
                .that(mUserManagerState.getCanForwardToProfileIdMap(mMockIntent)).isEqualTo(
                        canForwardToProfileIdMapBeforeIntent);
    }

    @Test
    public void testOnProfileStatusChange_actionProfileAvailable_profileNotInitialised() {
        if (!SdkLevel.isAtLeastV()) return;
        UserId currentUser = UserId.of(mSystemUser);
        UserId managedUser = UserId.of(mManagedUser);
        UserId privateUser = UserId.of(mPrivateUser);
        final List<ResolveInfo> mMockResolveInfoList = Lists.newArrayList(mMockInfo1, mMockInfo2);
        when(mMockPackageManager.queryIntentActivitiesAsUser(mMockIntent,
                PackageManager.MATCH_DEFAULT_ONLY, mSystemUser)).thenReturn(
                mMockResolveInfoList);

        // Private user will not be initialised if it is in quiet mode
        when(mMockUserManager.isQuietModeEnabled(mPrivateUser)).thenReturn(true);
        initializeUserManagerState(currentUser,
                Lists.newArrayList(mSystemUser, mManagedUser, mPrivateUser));

        // UserManagerState#mUserId and UserManagerState#mCanForwardToProfileIdMap will be empty
        // by default if the getters of these member variables have not been called
        List<UserId> userIdsBeforeIntent = new ArrayList<>(mUserManagerState.getUserIds());
        Map<UserId, Boolean> canForwardToProfileIdMapBeforeIntent = new HashMap<>(
                mUserManagerState.getCanForwardToProfileIdMap(mMockIntent));

        List<UserId> expectedUserIdsAfterIntent = Lists.newArrayList(currentUser, managedUser,
                privateUser);
        Map<UserId, Boolean> expectedCanForwardToProfileIdMapAfterIntent = new HashMap<>();
        expectedCanForwardToProfileIdMapAfterIntent.put(currentUser, true);
        expectedCanForwardToProfileIdMapAfterIntent.put(managedUser, true);
        expectedCanForwardToProfileIdMapAfterIntent.put(privateUser, true);

        String action = Intent.ACTION_PROFILE_AVAILABLE;
        mUserManagerState.onProfileActionStatusChange(action, privateUser);

        assertWithMessage(
                "UserIds list should not be same before and after receiving intent: " + action)
                .that(mUserManagerState.getUserIds()).isNotEqualTo(userIdsBeforeIntent);
        assertWithMessage("Unexpected changes to user id list on receiving intent: " + action)
                .that(mUserManagerState.getUserIds()).isEqualTo(expectedUserIdsAfterIntent);
        assertWithMessage(
                "CanForwardToLabelMap should not be same before and after receiving intent: "
                        + action)
                .that(mUserManagerState.getCanForwardToProfileIdMap(mMockIntent)).isNotEqualTo(
                        canForwardToProfileIdMapBeforeIntent);
        assertWithMessage(
                "Unexpected changes to canForwardToProfileIdMap on receiving intent: " + action)
                .that(mUserManagerState.getCanForwardToProfileIdMap(mMockIntent)).isEqualTo(
                        expectedCanForwardToProfileIdMapAfterIntent);
    }

    @Test
    public void testGetUserIdToLabelMap_systemUserAndManagedUser_PreV() {
        if (SdkLevel.isAtLeastV()) return;
        UserId currentUser = UserId.of(mSystemUser);
        initializeUserManagerState(currentUser,
                Lists.newArrayList(mSystemUser, mManagedUser));
        if (SdkLevel.isAtLeastT()) {
            DevicePolicyResourcesManager devicePolicyResourcesManager = mock(
                    DevicePolicyResourcesManager.class);
            when(mDevicePolicyManager.getResources()).thenReturn(devicePolicyResourcesManager);
            when(devicePolicyResourcesManager.getString(eq(PERSONAL_TAB), any())).thenReturn(
                    PERSONAL);
            when(devicePolicyResourcesManager.getString(eq(WORK_TAB), any())).thenReturn(WORK);
        }

        Map<UserId, String> userIdToLabelMap = mUserManagerState.getUserIdToLabelMap();

        assertWithMessage("Incorrect label returned for user id " + mSystemUser)
                .that(userIdToLabelMap.get(UserId.of(mSystemUser))).isEqualTo(PERSONAL);
        assertWithMessage("Incorrect label returned for user id " + mManagedUser)
                .that(userIdToLabelMap.get(UserId.of(mManagedUser))).isEqualTo(WORK);
    }

    @Test
    public void testGetUserIdToLabelMap_systemUserManagedUserPrivateUser_PostV() {
        if (!SdkLevel.isAtLeastV()) return;
        UserId currentUser = UserId.of(mSystemUser);
        initializeUserManagerState(currentUser,
                Lists.newArrayList(mSystemUser, mManagedUser, mPrivateUser));
        if (SdkLevel.isAtLeastT()) {
            DevicePolicyResourcesManager devicePolicyResourcesManager = mock(
                    DevicePolicyResourcesManager.class);
            when(mDevicePolicyManager.getResources()).thenReturn(devicePolicyResourcesManager);
            when(devicePolicyResourcesManager.getString(eq(PERSONAL_TAB), any())).thenReturn(
                    PERSONAL);
        }
        UserManager managedUserManager = getUserManagerForManagedUser();
        UserManager privateUserManager = getUserManagerForPrivateUser();
        when(managedUserManager.getProfileLabel()).thenReturn(WORK);
        when(privateUserManager.getProfileLabel()).thenReturn(PRIVATE);

        Map<UserId, String> userIdToLabelMap = mUserManagerState.getUserIdToLabelMap();

        assertWithMessage("Incorrect label returned for user id " + mSystemUser)
                .that(userIdToLabelMap.get(UserId.of(mSystemUser))).isEqualTo(PERSONAL);
        assertWithMessage("Incorrect label returned for user id " + mManagedUser)
                .that(userIdToLabelMap.get(UserId.of(mManagedUser))).isEqualTo(WORK);
        assertWithMessage("Incorrect label returned for user id " + mPrivateUser)
                .that(userIdToLabelMap.get(UserId.of(mPrivateUser))).isEqualTo(PRIVATE);
    }

    @Test
    public void testGetUserIdToBadgeMap_systemUserManagedUser_PreV() {
        if (SdkLevel.isAtLeastV()) return;
        UserId currentUser = UserId.of(mSystemUser);
        initializeUserManagerState(currentUser,
                Lists.newArrayList(mSystemUser, mManagedUser));
        Drawable workBadge = mock(Drawable.class);
        Resources resources = mock(Resources.class);
        when(mMockContext.getResources()).thenReturn(resources);
        when(mMockContext.getDrawable(R.drawable.ic_briefcase)).thenReturn(workBadge);
        if (SdkLevel.isAtLeastT()) {
            DevicePolicyResourcesManager devicePolicyResourcesManager = mock(
                    DevicePolicyResourcesManager.class);
            when(mDevicePolicyManager.getResources()).thenReturn(devicePolicyResourcesManager);
            when(devicePolicyResourcesManager.getDrawable(eq(WORK_PROFILE_ICON), eq(SOLID_COLORED),
                    any())).thenReturn(workBadge);
        }

        Map<UserId, Drawable> userIdToBadgeMap = mUserManagerState.getUserIdToBadgeMap();

        assertWithMessage("There should be no badge present for personal user")
                .that(userIdToBadgeMap.containsKey(UserId.of(mSystemUser))).isFalse();
        assertWithMessage("Incorrect badge returned for user id " + mManagedUser)
                .that(userIdToBadgeMap.get(UserId.of(mManagedUser))).isEqualTo(workBadge);
    }

    @Test
    public void testGetUserIdToBadgeMap_systemUserManagedUserPrivateUser_PostV() {
        if (!SdkLevel.isAtLeastV()) return;
        UserId currentUser = UserId.of(mSystemUser);
        initializeUserManagerState(currentUser,
                Lists.newArrayList(mSystemUser, mManagedUser, mPrivateUser));
        Drawable workBadge = mock(Drawable.class);
        Drawable privateBadge = mock(Drawable.class);
        UserManager managedUserManager = getUserManagerForManagedUser();
        UserManager privateUserManager = getUserManagerForPrivateUser();
        when(managedUserManager.getUserBadge()).thenReturn(workBadge);
        when(privateUserManager.getUserBadge()).thenReturn(privateBadge);

        Map<UserId, Drawable> userIdToBadgeMap = mUserManagerState.getUserIdToBadgeMap();

        assertWithMessage("There should be no badge present for personal user")
                .that(userIdToBadgeMap.get(UserId.of(mSystemUser))).isNull();
        assertWithMessage("Incorrect badge returned for user id " + mManagedUser)
                .that(userIdToBadgeMap.get(UserId.of(mManagedUser))).isEqualTo(workBadge);
        assertWithMessage("Incorrect badge returned for user id " + mPrivateUser)
                .that(userIdToBadgeMap.get(UserId.of(mPrivateUser))).isEqualTo(privateBadge);
    }

    private void initializeUserManagerState(UserId current, List<UserHandle> usersOnDevice) {
        when(mMockUserManager.getUserProfiles()).thenReturn(usersOnDevice);
        TestConfigStore testConfigStore = new TestConfigStore();
        testConfigStore.enablePrivateSpaceInPhotoPicker();
        mUserManagerState = new UserManagerState.RuntimeUserManagerState(mMockContext, current,
                true, testConfigStore);
    }

    private UserManager getUserManagerForManagedUser() {
        Context managedUserContext = mock(Context.class);
        when(mMockContext.createContextAsUser(mManagedUser, 0)).thenReturn(managedUserContext);
        UserManager managedUserManager = mock(UserManager.class);
        when(managedUserContext.getSystemServiceName(UserManager.class))
                .thenReturn("managedUserManager");
        when(managedUserContext.getSystemService(UserManager.class)).thenReturn(managedUserManager);
        return managedUserManager;
    }

    private UserManager getUserManagerForPrivateUser() {
        Context privateUserContext = mock(Context.class);
        when(mMockContext.createContextAsUser(mPrivateUser, 0)).thenReturn(privateUserContext);
        UserManager privateUserManager = mock(UserManager.class);
        when(privateUserContext.getSystemServiceName(UserManager.class))
                .thenReturn("privateUserManager");
        when(privateUserContext.getSystemService(UserManager.class)).thenReturn(privateUserManager);
        return privateUserManager;
    }
}
