Loading services/core/java/com/android/server/pm/UserFilter.java 0 → 100644 +156 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 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.server.pm; import static android.content.pm.UserInfo.flagsToString; import android.annotation.Nullable; import android.content.pm.UserInfo; import android.content.pm.UserInfo.UserInfoFlag; import java.util.Objects; /** * Simple POJO use to filter user in methods like {@code getUsers()} or {@code hasUser()}. */ public final class UserFilter { private final boolean mIncludePartial; private final boolean mIncludeDying; private final @UserInfoFlag int mRequiredFlags; private UserFilter(Builder builder) { mIncludePartial = builder.mIncludePartial; mIncludeDying = builder.mIncludeDying; mRequiredFlags = builder.mRequiredFlags; } /** * Returns {@code true} if the given user matches the filter (and always {@code false} if it's * {@code null}). */ boolean matches(DeathPredictor deathPredictor, @Nullable UserInfo user) { Objects.requireNonNull(deathPredictor, "deathPredictor cannot be null"); if (user == null) { return false; } // Check below is the "legacy" checks from getUsersInternal(), but with inverted logic // (!include instead of exclude) if ((!mIncludePartial && user.partial) || user.preCreated // Not supported anymore, so ignored by filter || (!mIncludeDying && deathPredictor.isDying(user))) { return false; } // Check flags return (user.flags & mRequiredFlags) == mRequiredFlags; } @Override public int hashCode() { return Objects.hash(mIncludeDying, mIncludePartial, mRequiredFlags); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } UserFilter other = (UserFilter) obj; return mIncludeDying == other.mIncludeDying && mIncludePartial == other.mIncludePartial && mRequiredFlags == other.mRequiredFlags; } @Override public String toString() { StringBuilder string = new StringBuilder("UserFilter["); if (mIncludePartial) { string.append("includePartial, "); } if (mIncludeDying) { string.append("includeDying, "); } if (mRequiredFlags != 0) { string.append("requiredFlags=").append(flagsToString(mRequiredFlags)); } else { // using else to avoid ending with , string.append("noRequiredFlags"); } return string.append(']').toString(); } /** * Gets a {@link Builder} instance. * * <p>For now it's always returning a new one, but eventually it could be optimized (for * example, reusing the same builder that's backed by a {@link ThreadLocal} instance. */ public static Builder builder() { return new Builder(); } /* * Bob, the Builder! * * <p>By default, it includes all users, without any filtering. */ public static final class Builder { private boolean mIncludePartial; private boolean mIncludeDying; private @UserInfoFlag int mRequiredFlags; private Builder() { } /** Returns a new {@code UserFilter}, */ public UserFilter build() { return new UserFilter(this); } /** Filter will Include partial users. */ public Builder withPartialUsers() { mIncludePartial = true; return this; } /** Filter will Include dying users. */ public Builder withDyingUsers() { mIncludeDying = true; return this; } /** When set, filter will only include users whose flags contain the given flags */ public Builder setRequiredFlags(@UserInfoFlag int flags) { mRequiredFlags = flags; return this; } } /** Used to decide if a user is dying, as that information is not present in the user itself. */ interface DeathPredictor { /** Returns {@code true} if the poor user is indeed dying... */ boolean isDying(UserInfo userInfo); } } services/core/java/com/android/server/pm/UserManagerService.java +63 −1 Original line number Diff line number Diff line Loading @@ -184,6 +184,7 @@ import com.android.server.StorageManagerInternal; import com.android.server.SystemService; import com.android.server.am.UserState; import com.android.server.locksettings.LockSettingsInternal; import com.android.server.pm.UserFilter.DeathPredictor; import com.android.server.pm.UserManagerInternal.UserLifecycleListener; import com.android.server.pm.UserManagerInternal.UserRestrictionsListener; import com.android.server.storage.DeviceStorageMonitorInternal; Loading Loading @@ -228,6 +229,7 @@ import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Function; /** * Service for {@link UserManager}. Loading Loading @@ -583,6 +585,10 @@ public class UserManagerService extends IUserManager.Stub { @GuardedBy("mUsersLock") private final SparseBooleanArray mRemovingUserIds = new SparseBooleanArray(); /** Used on methods that take a UserFilter (like {@link #getUsers(UserFilter)}) */ @GuardedBy("mUsersLock") private final DeathPredictor mDeathPredictor = user -> mRemovingUserIds.get(user.id); /** * Queue of recently removed userIds. Used for recycling of userIds */ Loading Loading @@ -1649,7 +1655,11 @@ public class UserManagerService extends IUserManager.Stub { } // Used by cmd users @NonNull List<UserInfo> getUsersWithUnresolvedNames(boolean excludePartial, /** * @deprecated should use {@link #getUsers(UserFilter)} instead. */ @Deprecated List<UserInfo> getUsersWithUnresolvedNames(boolean excludePartial, boolean excludeDying) { checkCreateUsersPermission("get users with unresolved names"); return getUsersInternal(excludePartial, excludeDying, /* resolveNullNames= */ false); Loading Loading @@ -1680,6 +1690,58 @@ public class UserManagerService extends IUserManager.Stub { } } /** Gets the users that match the given {@code filter}. */ List<UserInfo> getUsers(UserFilter filter) { return getUsersInternal(filter, /* converter= */ null); } /** * Gets the converted users that match the given {@code filter}. * * <p>Typically used with {@link #userWithName(UserInfo)} resolve {@code null} names. */ @VisibleForTesting List<UserInfo> getUsers(UserFilter filter, Function<UserInfo, UserInfo> converter) { Objects.requireNonNull(converter, "converter cannot be null"); return getUsersInternal(filter, converter); } private List<UserInfo> getUsersInternal(UserFilter filter, @Nullable Function<UserInfo, UserInfo> converter) { Objects.requireNonNull(filter, "filter cannot be null"); synchronized (mUsersLock) { ArrayList<UserInfo> users = new ArrayList<>(mUsers.size()); int userSize = mUsers.size(); for (int i = 0; i < userSize; i++) { UserInfo user = mUsers.valueAt(i).info; if (filter.matches(mDeathPredictor, user)) { if (converter == null) { users.add(user); } else { users.add(converter.apply(user)); } } } return users; } } /** Gets the number of users that matches the given {@code filter}. */ int getNumberOfUsers(UserFilter filter) { Objects.requireNonNull(filter, "filter cannot be null"); int number = 0; synchronized (mUsersLock) { int userSize = mUsers.size(); for (int i = 0; i < userSize; i++) { UserInfo user = mUsers.valueAt(i).info; if (filter.matches(mDeathPredictor, user)) { number++; } } } return number; } @Override public List<UserInfo> getProfiles(@UserIdInt int userId, boolean enabledOnly) { boolean returnFullInfo; Loading services/tests/mockingservicestests/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -48,6 +48,7 @@ android_test { "androidx.test.ext.truth", "flag-junit", "frameworks-base-testutils", "guava-android-testlib", // for EqualsTester "hamcrest-library", "kotlin-test", "mockingservicestests-utils-mockito", Loading services/tests/mockingservicestests/src/com/android/server/pm/UserFilterTest.java 0 → 100644 +268 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 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.server.pm; import static android.content.pm.UserInfo.FLAG_ADMIN; import static android.content.pm.UserInfo.FLAG_FULL; import static android.content.pm.UserInfo.FLAG_MAIN; import static android.content.pm.UserInfo.FLAG_SYSTEM; import static android.os.UserHandle.USER_SYSTEM; import static android.os.UserManager.USER_TYPE_FULL_SYSTEM; import static android.os.UserManager.USER_TYPE_SYSTEM_HEADLESS; import static android.os.UserManager.USER_TYPE_FULL_SECONDARY; import static org.junit.Assert.assertThrows; import android.annotation.UserIdInt; import android.content.pm.UserInfo; import android.content.pm.UserInfo.UserInfoFlag; import com.android.server.pm.UserFilter.Builder; import com.android.server.pm.UserFilter.DeathPredictor; import com.google.common.testing.EqualsTester; import com.google.common.truth.Expect; import org.junit.Rule; import org.junit.Test; public final class UserFilterTest { private static final @UserIdInt int ADMIN_USER_ID = 4; private static final @UserIdInt int NON_ADMIN_USER_ID = 8; private static final @UserIdInt int FLAGLESS_USER_ID = 15; private static final @UserIdInt int PARTIAL_USER_ID = 16; private static final @UserIdInt int DYING_USER_ID = 23; private static final @UserIdInt int PRE_CREATED_USER_ID = 42; private static final DeathPredictor NOSTRADAMUS = user -> user.id == DYING_USER_ID; private final UserInfo mNullUser = null; // Note: not setting FLAG_PRIMARY on system user as it's deprecated (and not really used private final UserInfo mFullSystemUser = createUser(USER_SYSTEM, "full system user", FLAG_ADMIN | FLAG_FULL | FLAG_SYSTEM, USER_TYPE_FULL_SYSTEM); private final UserInfo mHeadlessSystemUser = createUser(USER_SYSTEM, "headless system user", FLAG_ADMIN | FLAG_SYSTEM, USER_TYPE_SYSTEM_HEADLESS); private final UserInfo mAdminUser = createUser(ADMIN_USER_ID, "admin user", FLAG_ADMIN | FLAG_FULL , USER_TYPE_FULL_SECONDARY); private final UserInfo mNonAdminUser = createSecondaryUser(NON_ADMIN_USER_ID, "non-admin user"); private final UserInfo mFlagLessUser = createUser(FLAGLESS_USER_ID, "flagless user", /* flags= */ 0, USER_TYPE_FULL_SECONDARY); private final UserInfo mDyingUser = createSecondaryUser(DYING_USER_ID, "dying user"); // Users below are defined in the constructor because we need to set some properties after // they're instantiated private final UserInfo mPartialUser; // NOTE: pre-created users are not supported anymore, so they shouldn't be included on any // result private final UserInfo mPreCreatedUser; @Rule public final Expect expect = Expect.create(); public UserFilterTest() { mPartialUser = createSecondaryUser(PARTIAL_USER_ID, "partial user"); mPartialUser.partial = true; mPreCreatedUser = createSecondaryUser(PRE_CREATED_USER_ID, "pre-created user"); mPreCreatedUser.preCreated = true; } @Test public void testEquals() { var defaultBuilder = createBuilder(); UserFilter default1 = defaultBuilder.build(); UserFilter default2 = defaultBuilder.build(); var builderWithEverything = createBuilder() .withPartialUsers() .withDyingUsers() .setRequiredFlags(FLAG_MAIN); UserFilter withEverything1 = builderWithEverything.build(); UserFilter withEverything2 = builderWithEverything.build(); new EqualsTester() .addEqualityGroup(default1, default2) .addEqualityGroup(withEverything1, withEverything2) .testEquals(); } @Test public void testNullDeathPredictor() { UserFilter filter = createBuilder().build(); assertThrows(NullPointerException.class, ()-> filter.matches(null, mFullSystemUser)); assertThrows(NullPointerException.class, ()-> filter.matches(null, null)); } @Test public void testDefaultFilter() { UserFilter filter = createBuilder().build(); expectMatches(filter, mNullUser, false); expectMatches(filter, mFullSystemUser, true); expectMatches(filter, mHeadlessSystemUser, true); expectMatches(filter, mAdminUser, true); expectMatches(filter, mNonAdminUser, true); expectMatches(filter, mFlagLessUser, true); expectMatches(filter, mPartialUser, false); expectMatches(filter, mDyingUser, false); expectMatches(filter, mPreCreatedUser, false); } @Test public void testIncludePartial() { UserFilter filter = createBuilder() .withPartialUsers() .build(); expectMatches(filter, mNullUser, false); expectMatches(filter, mFullSystemUser, true); expectMatches(filter, mHeadlessSystemUser, true); expectMatches(filter, mAdminUser, true); expectMatches(filter, mNonAdminUser, true); expectMatches(filter, mFlagLessUser, true); expectMatches(filter, mPartialUser, true); expectMatches(filter, mDyingUser, false); expectMatches(filter, mPreCreatedUser, false); } @Test public void testIncludeDying() { UserFilter filter = createBuilder() .withDyingUsers() .build(); expectMatches(filter, mNullUser, false); expectMatches(filter, mFullSystemUser, true); expectMatches(filter, mHeadlessSystemUser, true); expectMatches(filter, mAdminUser, true); expectMatches(filter, mNonAdminUser, true); expectMatches(filter, mFlagLessUser, true); expectMatches(filter, mPartialUser, false); expectMatches(filter, mDyingUser, true); expectMatches(filter, mPreCreatedUser, false); } @Test public void testIncludePreCreated() { UserFilter filter = createBuilder() .build(); expectMatches(filter, mNullUser, false); expectMatches(filter, mFullSystemUser, true); expectMatches(filter, mHeadlessSystemUser, true); expectMatches(filter, mAdminUser, true); expectMatches(filter, mNonAdminUser, true); expectMatches(filter, mFlagLessUser, true); expectMatches(filter, mPartialUser, false); expectMatches(filter, mDyingUser, false); expectMatches(filter, mPreCreatedUser, false); } @Test public void testIncludeAllDefectiveUsers() { UserFilter filter = createBuilder() .withPartialUsers() .withDyingUsers() .build(); expectMatches(filter, mNullUser, false); expectMatches(filter, mFullSystemUser, true); expectMatches(filter, mHeadlessSystemUser, true); expectMatches(filter, mAdminUser, true); expectMatches(filter, mNonAdminUser, true); expectMatches(filter, mFlagLessUser, true); expectMatches(filter, mPartialUser, true); expectMatches(filter, mDyingUser, true); expectMatches(filter, mPreCreatedUser, false); } @Test public void testAdminsOnly() { UserFilter filter = createBuilder() .setRequiredFlags(FLAG_ADMIN) .build(); expectMatches(filter, mNullUser, false); expectMatches(filter, mFullSystemUser, true); expectMatches(filter, mHeadlessSystemUser, true); expectMatches(filter, mAdminUser, true); expectMatches(filter, mNonAdminUser, false); expectMatches(filter, mFlagLessUser, false); expectMatches(filter, mPartialUser, false); expectMatches(filter, mDyingUser, false); expectMatches(filter, mPreCreatedUser, false); } @Test public void testFullUsersOnly() { UserFilter filter = createBuilder() .setRequiredFlags(FLAG_FULL) .build(); expectMatches(filter, mNullUser, false); expectMatches(filter, mFullSystemUser, true); expectMatches(filter, mHeadlessSystemUser, false); expectMatches(filter, mAdminUser, true); expectMatches(filter, mNonAdminUser, true); expectMatches(filter, mFlagLessUser, false); expectMatches(filter, mPartialUser, false); expectMatches(filter, mDyingUser, false); expectMatches(filter, mPreCreatedUser, false); } @Test public void testFullAdminsOnly() { UserFilter filter = createBuilder() .setRequiredFlags(FLAG_FULL | FLAG_ADMIN) .build(); expectMatches(filter, mNullUser, false); expectMatches(filter, mFullSystemUser, true); expectMatches(filter, mHeadlessSystemUser, false); expectMatches(filter, mAdminUser, true); expectMatches(filter, mNonAdminUser, false); expectMatches(filter, mFlagLessUser, false); expectMatches(filter, mPartialUser, false); expectMatches(filter, mDyingUser, false); expectMatches(filter, mPreCreatedUser, false); } private void expectMatches(UserFilter filter, UserInfo user, boolean value) { expect.withMessage("matches %s", user).that(filter.matches(NOSTRADAMUS, user)) .isEqualTo(value); } private static UserInfo createUser(@UserIdInt int userId, String name, @UserInfoFlag int flags, String userType) { return new UserInfo(userId, name, /* iconPath= */ null, flags, userType); } private static UserInfo createSecondaryUser(@UserIdInt int userId, String name) { return createUser(userId, name, FLAG_FULL, USER_TYPE_FULL_SECONDARY); } private Builder createBuilder() { return UserFilter.builder(); } } services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceMockedTest.java +117 −0 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
services/core/java/com/android/server/pm/UserFilter.java 0 → 100644 +156 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 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.server.pm; import static android.content.pm.UserInfo.flagsToString; import android.annotation.Nullable; import android.content.pm.UserInfo; import android.content.pm.UserInfo.UserInfoFlag; import java.util.Objects; /** * Simple POJO use to filter user in methods like {@code getUsers()} or {@code hasUser()}. */ public final class UserFilter { private final boolean mIncludePartial; private final boolean mIncludeDying; private final @UserInfoFlag int mRequiredFlags; private UserFilter(Builder builder) { mIncludePartial = builder.mIncludePartial; mIncludeDying = builder.mIncludeDying; mRequiredFlags = builder.mRequiredFlags; } /** * Returns {@code true} if the given user matches the filter (and always {@code false} if it's * {@code null}). */ boolean matches(DeathPredictor deathPredictor, @Nullable UserInfo user) { Objects.requireNonNull(deathPredictor, "deathPredictor cannot be null"); if (user == null) { return false; } // Check below is the "legacy" checks from getUsersInternal(), but with inverted logic // (!include instead of exclude) if ((!mIncludePartial && user.partial) || user.preCreated // Not supported anymore, so ignored by filter || (!mIncludeDying && deathPredictor.isDying(user))) { return false; } // Check flags return (user.flags & mRequiredFlags) == mRequiredFlags; } @Override public int hashCode() { return Objects.hash(mIncludeDying, mIncludePartial, mRequiredFlags); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } UserFilter other = (UserFilter) obj; return mIncludeDying == other.mIncludeDying && mIncludePartial == other.mIncludePartial && mRequiredFlags == other.mRequiredFlags; } @Override public String toString() { StringBuilder string = new StringBuilder("UserFilter["); if (mIncludePartial) { string.append("includePartial, "); } if (mIncludeDying) { string.append("includeDying, "); } if (mRequiredFlags != 0) { string.append("requiredFlags=").append(flagsToString(mRequiredFlags)); } else { // using else to avoid ending with , string.append("noRequiredFlags"); } return string.append(']').toString(); } /** * Gets a {@link Builder} instance. * * <p>For now it's always returning a new one, but eventually it could be optimized (for * example, reusing the same builder that's backed by a {@link ThreadLocal} instance. */ public static Builder builder() { return new Builder(); } /* * Bob, the Builder! * * <p>By default, it includes all users, without any filtering. */ public static final class Builder { private boolean mIncludePartial; private boolean mIncludeDying; private @UserInfoFlag int mRequiredFlags; private Builder() { } /** Returns a new {@code UserFilter}, */ public UserFilter build() { return new UserFilter(this); } /** Filter will Include partial users. */ public Builder withPartialUsers() { mIncludePartial = true; return this; } /** Filter will Include dying users. */ public Builder withDyingUsers() { mIncludeDying = true; return this; } /** When set, filter will only include users whose flags contain the given flags */ public Builder setRequiredFlags(@UserInfoFlag int flags) { mRequiredFlags = flags; return this; } } /** Used to decide if a user is dying, as that information is not present in the user itself. */ interface DeathPredictor { /** Returns {@code true} if the poor user is indeed dying... */ boolean isDying(UserInfo userInfo); } }
services/core/java/com/android/server/pm/UserManagerService.java +63 −1 Original line number Diff line number Diff line Loading @@ -184,6 +184,7 @@ import com.android.server.StorageManagerInternal; import com.android.server.SystemService; import com.android.server.am.UserState; import com.android.server.locksettings.LockSettingsInternal; import com.android.server.pm.UserFilter.DeathPredictor; import com.android.server.pm.UserManagerInternal.UserLifecycleListener; import com.android.server.pm.UserManagerInternal.UserRestrictionsListener; import com.android.server.storage.DeviceStorageMonitorInternal; Loading Loading @@ -228,6 +229,7 @@ import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Function; /** * Service for {@link UserManager}. Loading Loading @@ -583,6 +585,10 @@ public class UserManagerService extends IUserManager.Stub { @GuardedBy("mUsersLock") private final SparseBooleanArray mRemovingUserIds = new SparseBooleanArray(); /** Used on methods that take a UserFilter (like {@link #getUsers(UserFilter)}) */ @GuardedBy("mUsersLock") private final DeathPredictor mDeathPredictor = user -> mRemovingUserIds.get(user.id); /** * Queue of recently removed userIds. Used for recycling of userIds */ Loading Loading @@ -1649,7 +1655,11 @@ public class UserManagerService extends IUserManager.Stub { } // Used by cmd users @NonNull List<UserInfo> getUsersWithUnresolvedNames(boolean excludePartial, /** * @deprecated should use {@link #getUsers(UserFilter)} instead. */ @Deprecated List<UserInfo> getUsersWithUnresolvedNames(boolean excludePartial, boolean excludeDying) { checkCreateUsersPermission("get users with unresolved names"); return getUsersInternal(excludePartial, excludeDying, /* resolveNullNames= */ false); Loading Loading @@ -1680,6 +1690,58 @@ public class UserManagerService extends IUserManager.Stub { } } /** Gets the users that match the given {@code filter}. */ List<UserInfo> getUsers(UserFilter filter) { return getUsersInternal(filter, /* converter= */ null); } /** * Gets the converted users that match the given {@code filter}. * * <p>Typically used with {@link #userWithName(UserInfo)} resolve {@code null} names. */ @VisibleForTesting List<UserInfo> getUsers(UserFilter filter, Function<UserInfo, UserInfo> converter) { Objects.requireNonNull(converter, "converter cannot be null"); return getUsersInternal(filter, converter); } private List<UserInfo> getUsersInternal(UserFilter filter, @Nullable Function<UserInfo, UserInfo> converter) { Objects.requireNonNull(filter, "filter cannot be null"); synchronized (mUsersLock) { ArrayList<UserInfo> users = new ArrayList<>(mUsers.size()); int userSize = mUsers.size(); for (int i = 0; i < userSize; i++) { UserInfo user = mUsers.valueAt(i).info; if (filter.matches(mDeathPredictor, user)) { if (converter == null) { users.add(user); } else { users.add(converter.apply(user)); } } } return users; } } /** Gets the number of users that matches the given {@code filter}. */ int getNumberOfUsers(UserFilter filter) { Objects.requireNonNull(filter, "filter cannot be null"); int number = 0; synchronized (mUsersLock) { int userSize = mUsers.size(); for (int i = 0; i < userSize; i++) { UserInfo user = mUsers.valueAt(i).info; if (filter.matches(mDeathPredictor, user)) { number++; } } } return number; } @Override public List<UserInfo> getProfiles(@UserIdInt int userId, boolean enabledOnly) { boolean returnFullInfo; Loading
services/tests/mockingservicestests/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -48,6 +48,7 @@ android_test { "androidx.test.ext.truth", "flag-junit", "frameworks-base-testutils", "guava-android-testlib", // for EqualsTester "hamcrest-library", "kotlin-test", "mockingservicestests-utils-mockito", Loading
services/tests/mockingservicestests/src/com/android/server/pm/UserFilterTest.java 0 → 100644 +268 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 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.server.pm; import static android.content.pm.UserInfo.FLAG_ADMIN; import static android.content.pm.UserInfo.FLAG_FULL; import static android.content.pm.UserInfo.FLAG_MAIN; import static android.content.pm.UserInfo.FLAG_SYSTEM; import static android.os.UserHandle.USER_SYSTEM; import static android.os.UserManager.USER_TYPE_FULL_SYSTEM; import static android.os.UserManager.USER_TYPE_SYSTEM_HEADLESS; import static android.os.UserManager.USER_TYPE_FULL_SECONDARY; import static org.junit.Assert.assertThrows; import android.annotation.UserIdInt; import android.content.pm.UserInfo; import android.content.pm.UserInfo.UserInfoFlag; import com.android.server.pm.UserFilter.Builder; import com.android.server.pm.UserFilter.DeathPredictor; import com.google.common.testing.EqualsTester; import com.google.common.truth.Expect; import org.junit.Rule; import org.junit.Test; public final class UserFilterTest { private static final @UserIdInt int ADMIN_USER_ID = 4; private static final @UserIdInt int NON_ADMIN_USER_ID = 8; private static final @UserIdInt int FLAGLESS_USER_ID = 15; private static final @UserIdInt int PARTIAL_USER_ID = 16; private static final @UserIdInt int DYING_USER_ID = 23; private static final @UserIdInt int PRE_CREATED_USER_ID = 42; private static final DeathPredictor NOSTRADAMUS = user -> user.id == DYING_USER_ID; private final UserInfo mNullUser = null; // Note: not setting FLAG_PRIMARY on system user as it's deprecated (and not really used private final UserInfo mFullSystemUser = createUser(USER_SYSTEM, "full system user", FLAG_ADMIN | FLAG_FULL | FLAG_SYSTEM, USER_TYPE_FULL_SYSTEM); private final UserInfo mHeadlessSystemUser = createUser(USER_SYSTEM, "headless system user", FLAG_ADMIN | FLAG_SYSTEM, USER_TYPE_SYSTEM_HEADLESS); private final UserInfo mAdminUser = createUser(ADMIN_USER_ID, "admin user", FLAG_ADMIN | FLAG_FULL , USER_TYPE_FULL_SECONDARY); private final UserInfo mNonAdminUser = createSecondaryUser(NON_ADMIN_USER_ID, "non-admin user"); private final UserInfo mFlagLessUser = createUser(FLAGLESS_USER_ID, "flagless user", /* flags= */ 0, USER_TYPE_FULL_SECONDARY); private final UserInfo mDyingUser = createSecondaryUser(DYING_USER_ID, "dying user"); // Users below are defined in the constructor because we need to set some properties after // they're instantiated private final UserInfo mPartialUser; // NOTE: pre-created users are not supported anymore, so they shouldn't be included on any // result private final UserInfo mPreCreatedUser; @Rule public final Expect expect = Expect.create(); public UserFilterTest() { mPartialUser = createSecondaryUser(PARTIAL_USER_ID, "partial user"); mPartialUser.partial = true; mPreCreatedUser = createSecondaryUser(PRE_CREATED_USER_ID, "pre-created user"); mPreCreatedUser.preCreated = true; } @Test public void testEquals() { var defaultBuilder = createBuilder(); UserFilter default1 = defaultBuilder.build(); UserFilter default2 = defaultBuilder.build(); var builderWithEverything = createBuilder() .withPartialUsers() .withDyingUsers() .setRequiredFlags(FLAG_MAIN); UserFilter withEverything1 = builderWithEverything.build(); UserFilter withEverything2 = builderWithEverything.build(); new EqualsTester() .addEqualityGroup(default1, default2) .addEqualityGroup(withEverything1, withEverything2) .testEquals(); } @Test public void testNullDeathPredictor() { UserFilter filter = createBuilder().build(); assertThrows(NullPointerException.class, ()-> filter.matches(null, mFullSystemUser)); assertThrows(NullPointerException.class, ()-> filter.matches(null, null)); } @Test public void testDefaultFilter() { UserFilter filter = createBuilder().build(); expectMatches(filter, mNullUser, false); expectMatches(filter, mFullSystemUser, true); expectMatches(filter, mHeadlessSystemUser, true); expectMatches(filter, mAdminUser, true); expectMatches(filter, mNonAdminUser, true); expectMatches(filter, mFlagLessUser, true); expectMatches(filter, mPartialUser, false); expectMatches(filter, mDyingUser, false); expectMatches(filter, mPreCreatedUser, false); } @Test public void testIncludePartial() { UserFilter filter = createBuilder() .withPartialUsers() .build(); expectMatches(filter, mNullUser, false); expectMatches(filter, mFullSystemUser, true); expectMatches(filter, mHeadlessSystemUser, true); expectMatches(filter, mAdminUser, true); expectMatches(filter, mNonAdminUser, true); expectMatches(filter, mFlagLessUser, true); expectMatches(filter, mPartialUser, true); expectMatches(filter, mDyingUser, false); expectMatches(filter, mPreCreatedUser, false); } @Test public void testIncludeDying() { UserFilter filter = createBuilder() .withDyingUsers() .build(); expectMatches(filter, mNullUser, false); expectMatches(filter, mFullSystemUser, true); expectMatches(filter, mHeadlessSystemUser, true); expectMatches(filter, mAdminUser, true); expectMatches(filter, mNonAdminUser, true); expectMatches(filter, mFlagLessUser, true); expectMatches(filter, mPartialUser, false); expectMatches(filter, mDyingUser, true); expectMatches(filter, mPreCreatedUser, false); } @Test public void testIncludePreCreated() { UserFilter filter = createBuilder() .build(); expectMatches(filter, mNullUser, false); expectMatches(filter, mFullSystemUser, true); expectMatches(filter, mHeadlessSystemUser, true); expectMatches(filter, mAdminUser, true); expectMatches(filter, mNonAdminUser, true); expectMatches(filter, mFlagLessUser, true); expectMatches(filter, mPartialUser, false); expectMatches(filter, mDyingUser, false); expectMatches(filter, mPreCreatedUser, false); } @Test public void testIncludeAllDefectiveUsers() { UserFilter filter = createBuilder() .withPartialUsers() .withDyingUsers() .build(); expectMatches(filter, mNullUser, false); expectMatches(filter, mFullSystemUser, true); expectMatches(filter, mHeadlessSystemUser, true); expectMatches(filter, mAdminUser, true); expectMatches(filter, mNonAdminUser, true); expectMatches(filter, mFlagLessUser, true); expectMatches(filter, mPartialUser, true); expectMatches(filter, mDyingUser, true); expectMatches(filter, mPreCreatedUser, false); } @Test public void testAdminsOnly() { UserFilter filter = createBuilder() .setRequiredFlags(FLAG_ADMIN) .build(); expectMatches(filter, mNullUser, false); expectMatches(filter, mFullSystemUser, true); expectMatches(filter, mHeadlessSystemUser, true); expectMatches(filter, mAdminUser, true); expectMatches(filter, mNonAdminUser, false); expectMatches(filter, mFlagLessUser, false); expectMatches(filter, mPartialUser, false); expectMatches(filter, mDyingUser, false); expectMatches(filter, mPreCreatedUser, false); } @Test public void testFullUsersOnly() { UserFilter filter = createBuilder() .setRequiredFlags(FLAG_FULL) .build(); expectMatches(filter, mNullUser, false); expectMatches(filter, mFullSystemUser, true); expectMatches(filter, mHeadlessSystemUser, false); expectMatches(filter, mAdminUser, true); expectMatches(filter, mNonAdminUser, true); expectMatches(filter, mFlagLessUser, false); expectMatches(filter, mPartialUser, false); expectMatches(filter, mDyingUser, false); expectMatches(filter, mPreCreatedUser, false); } @Test public void testFullAdminsOnly() { UserFilter filter = createBuilder() .setRequiredFlags(FLAG_FULL | FLAG_ADMIN) .build(); expectMatches(filter, mNullUser, false); expectMatches(filter, mFullSystemUser, true); expectMatches(filter, mHeadlessSystemUser, false); expectMatches(filter, mAdminUser, true); expectMatches(filter, mNonAdminUser, false); expectMatches(filter, mFlagLessUser, false); expectMatches(filter, mPartialUser, false); expectMatches(filter, mDyingUser, false); expectMatches(filter, mPreCreatedUser, false); } private void expectMatches(UserFilter filter, UserInfo user, boolean value) { expect.withMessage("matches %s", user).that(filter.matches(NOSTRADAMUS, user)) .isEqualTo(value); } private static UserInfo createUser(@UserIdInt int userId, String name, @UserInfoFlag int flags, String userType) { return new UserInfo(userId, name, /* iconPath= */ null, flags, userType); } private static UserInfo createSecondaryUser(@UserIdInt int userId, String name) { return createUser(userId, name, FLAG_FULL, USER_TYPE_FULL_SECONDARY); } private Builder createBuilder() { return UserFilter.builder(); } }
services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceMockedTest.java +117 −0 File changed.Preview size limit exceeded, changes collapsed. Show changes