From 9ff2a43ae5d1a40937a539bcf36ce566b50631a8 Mon Sep 17 00:00:00 2001 From: Felipe Leme Date: Thu, 8 Jul 2021 14:43:37 -0700 Subject: [PATCH] Added managed profile owners on 'cmd device_policy list-owners' Test: adb shell cmd device_policy list-owners Test: atest FrameworksMockingServicesTests:com.android.server.devicepolicy.OwnerShellDataTest Fixes: 193172204 Change-Id: I45e46d55514c8db4749a7098e18cd84acb2e0a80 --- .../DevicePolicyManagerService.java | 27 +++- ...evicePolicyManagerServiceShellCommand.java | 9 +- .../server/devicepolicy/OwnerShellData.java | 98 +++++++++++++ .../android/server/devicepolicy/Owners.java | 28 +--- .../devicepolicy/FactoryResetterTest.java | 2 +- .../devicepolicy/OwnerShellDataTest.java | 129 ++++++++++++++++++ 6 files changed, 260 insertions(+), 33 deletions(-) create mode 100644 services/devicepolicy/java/com/android/server/devicepolicy/OwnerShellData.java create mode 100644 services/tests/mockingservicestests/src/com/android/server/devicepolicy/OwnerShellDataTest.java diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 193d92a3b2ff9..70219d2503a38 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -327,7 +327,6 @@ import com.android.server.PersistentDataBlockManagerInternal; import com.android.server.SystemServerInitThreadPool; import com.android.server.SystemService; import com.android.server.devicepolicy.ActiveAdmin.TrustAgentInfo; -import com.android.server.devicepolicy.Owners.OwnerDto; import com.android.server.inputmethod.InputMethodManagerInternal; import com.android.server.net.NetworkPolicyManagerInternal; import com.android.server.pm.RestrictionsSet; @@ -1259,17 +1258,37 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } // Used by DevicePolicyManagerServiceShellCommand - List listAllOwners() { + List listAllOwners() { Preconditions.checkCallAuthorization( hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS)); return mInjector.binderWithCleanCallingIdentity(() -> { - List owners = mOwners.listAllOwners(); + SparseArray userData; + + // Gets the owners of "full users" first (device owner and profile owners) + List owners = mOwners.listAllOwners(); synchronized (getLockObject()) { for (int i = 0; i < owners.size(); i++) { - OwnerDto owner = owners.get(i); + OwnerShellData owner = owners.get(i); owner.isAffiliated = isUserAffiliatedWithDeviceLocked(owner.userId); } + userData = mUserData; } + + // Then the owners of profile users (managed profiles) + for (int i = 0; i < userData.size(); i++) { + DevicePolicyData policyData = mUserData.valueAt(i); + int userId = userData.keyAt(i); + int parentUserId = mUserManagerInternal.getProfileParentId(userId); + boolean isProfile = parentUserId != userId; + if (!isProfile) continue; + for (int j = 0; j < policyData.mAdminList.size(); j++) { + ActiveAdmin admin = policyData.mAdminList.get(j); + OwnerShellData owner = OwnerShellData.forManagedProfileOwner(userId, + parentUserId, admin.info.getComponent()); + owners.add(owner); + } + } + return owners; }); } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java index a2db6aaca3df2..85fe65ca55635 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java @@ -22,8 +22,6 @@ import android.os.ShellCommand; import android.os.SystemClock; import android.os.UserHandle; -import com.android.server.devicepolicy.Owners.OwnerDto; - import java.io.PrintWriter; import java.util.Collection; import java.util.List; @@ -205,12 +203,12 @@ final class DevicePolicyManagerServiceShellCommand extends ShellCommand { } private int runListOwners(PrintWriter pw) { - List owners = mService.listAllOwners(); + List owners = mService.listAllOwners(); int size = printAndGetSize(pw, owners, "owner"); if (size == 0) return 0; for (int i = 0; i < size; i++) { - OwnerDto owner = owners.get(i); + OwnerShellData owner = owners.get(i); pw.printf("User %2d: admin=%s", owner.userId, owner.admin.flattenToShortString()); if (owner.isDeviceOwner) { pw.print(",DeviceOwner"); @@ -218,6 +216,9 @@ final class DevicePolicyManagerServiceShellCommand extends ShellCommand { if (owner.isProfileOwner) { pw.print(",ProfileOwner"); } + if (owner.isManagedProfileOwner) { + pw.printf(",ManagedProfileOwner(parentUserId=%d)", owner.parentUserId); + } if (owner.isAffiliated) { pw.print(",Affiliated"); } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/OwnerShellData.java b/services/devicepolicy/java/com/android/server/devicepolicy/OwnerShellData.java new file mode 100644 index 0000000000000..b98c3dc2ac076 --- /dev/null +++ b/services/devicepolicy/java/com/android/server/devicepolicy/OwnerShellData.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2021 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.devicepolicy; + +import static android.os.UserHandle.USER_NULL; + +import android.annotation.UserIdInt; +import android.content.ComponentName; + +import com.android.internal.util.Preconditions; + +import java.util.Objects; + +/** + * Data-transfer object used by {@link DevicePolicyManagerServiceShellCommand}. + */ +final class OwnerShellData { + + public final @UserIdInt int userId; + public final @UserIdInt int parentUserId; + public final ComponentName admin; + public final boolean isDeviceOwner; + public final boolean isProfileOwner; + public final boolean isManagedProfileOwner; + public boolean isAffiliated; + + // NOTE: class is too simple to require a Builder (not to mention isAffiliated is mutable) + private OwnerShellData(@UserIdInt int userId, @UserIdInt int parentUserId, ComponentName admin, + boolean isDeviceOwner, boolean isProfileOwner, boolean isManagedProfileOwner) { + Preconditions.checkArgument(userId != USER_NULL, "userId cannot be USER_NULL"); + this.userId = userId; + this.parentUserId = parentUserId; + this.admin = Objects.requireNonNull(admin, "admin must not be null"); + this.isDeviceOwner = isDeviceOwner; + this.isProfileOwner = isProfileOwner; + this.isManagedProfileOwner = isManagedProfileOwner; + if (isManagedProfileOwner) { + Preconditions.checkArgument(parentUserId != USER_NULL, + "parentUserId cannot be USER_NULL for managed profile owner"); + Preconditions.checkArgument(parentUserId != userId, + "cannot be parent of itself (%d)", userId); + } + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(getClass().getSimpleName()) + .append("[userId=").append(userId) + .append(",admin=").append(admin.flattenToShortString()); + if (isDeviceOwner) { + sb.append(",deviceOwner"); + } + if (isProfileOwner) { + sb.append(",isProfileOwner"); + } + if (isManagedProfileOwner) { + sb.append(",isManagedProfileOwner"); + } + if (parentUserId != USER_NULL) { + sb.append(",parentUserId=").append(parentUserId); + } + if (isAffiliated) { + sb.append(",isAffiliated"); + } + return sb.append(']').toString(); + } + + static OwnerShellData forDeviceOwner(@UserIdInt int userId, ComponentName admin) { + return new OwnerShellData(userId, /* parentUserId= */ USER_NULL, admin, + /* isDeviceOwner= */ true, /* isProfileOwner= */ false, + /* isManagedProfileOwner= */ false); + } + + static OwnerShellData forUserProfileOwner(@UserIdInt int userId, ComponentName admin) { + return new OwnerShellData(userId, /* parentUserId= */ USER_NULL, admin, + /* isDeviceOwner= */ false, /* isProfileOwner= */ true, + /* isManagedProfileOwner= */ false); + } + + static OwnerShellData forManagedProfileOwner(@UserIdInt int userId, @UserIdInt int parentUserId, + ComponentName admin) { + return new OwnerShellData(userId, parentUserId, admin, /* isDeviceOwner= */ false, + /* isProfileOwner= */ false, /* isManagedProfileOwner= */ true); + } +} diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java index fd09e3f9cfd07..3584728a2e628 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java @@ -19,7 +19,6 @@ package com.android.server.devicepolicy; import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_DEFAULT; import android.annotation.Nullable; -import android.annotation.UserIdInt; import android.app.ActivityManagerInternal; import android.app.AppOpsManagerInternal; import android.app.admin.DevicePolicyManager.DeviceOwnerType; @@ -476,17 +475,16 @@ class Owners { } } - List listAllOwners() { - List owners = new ArrayList<>(); + List listAllOwners() { + List owners = new ArrayList<>(); synchronized (mLock) { if (mDeviceOwner != null) { - owners.add(new OwnerDto(mDeviceOwnerUserId, mDeviceOwner.admin, - /* isDeviceOwner= */ true)); + owners.add(OwnerShellData.forDeviceOwner(mDeviceOwnerUserId, mDeviceOwner.admin)); } for (int i = 0; i < mProfileOwners.size(); i++) { int userId = mProfileOwners.keyAt(i); OwnerInfo info = mProfileOwners.valueAt(i); - owners.add(new OwnerDto(userId, info.admin, /* isDeviceOwner= */ false)); + owners.add(OwnerShellData.forUserProfileOwner(userId, info.admin)); } } return owners; @@ -1236,24 +1234,6 @@ class Owners { } } - /** - * Data-transfer object used by {@link DevicePolicyManagerServiceShellCommand}. - */ - static final class OwnerDto { - public final @UserIdInt int userId; - public final ComponentName admin; - public final boolean isDeviceOwner; - public final boolean isProfileOwner; - public boolean isAffiliated; - - private OwnerDto(@UserIdInt int userId, ComponentName admin, boolean isDeviceOwner) { - this.userId = userId; - this.admin = Objects.requireNonNull(admin, "admin must not be null"); - this.isDeviceOwner = isDeviceOwner; - this.isProfileOwner = !isDeviceOwner; - } - } - public void dump(IndentingPrintWriter pw) { boolean needBlank = false; if (mDeviceOwner != null) { diff --git a/services/tests/mockingservicestests/src/com/android/server/devicepolicy/FactoryResetterTest.java b/services/tests/mockingservicestests/src/com/android/server/devicepolicy/FactoryResetterTest.java index 589a3497435e9..457c8db9fdf3b 100644 --- a/services/tests/mockingservicestests/src/com/android/server/devicepolicy/FactoryResetterTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/devicepolicy/FactoryResetterTest.java @@ -48,7 +48,7 @@ import org.mockito.MockitoSession; import org.mockito.quality.Strictness; /** - * Run it as {@code atest FrameworksMockingCoreTests:FactoryResetterTest} + * Run it as {@code atest FrameworksMockingServicesTests:FactoryResetterTest} */ @Presubmit public final class FactoryResetterTest { diff --git a/services/tests/mockingservicestests/src/com/android/server/devicepolicy/OwnerShellDataTest.java b/services/tests/mockingservicestests/src/com/android/server/devicepolicy/OwnerShellDataTest.java new file mode 100644 index 0000000000000..dd67d72080346 --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/devicepolicy/OwnerShellDataTest.java @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2021 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.devicepolicy; + +import static android.os.UserHandle.USER_NULL; + +import static com.google.common.truth.Truth.assertWithMessage; + +import static org.testng.Assert.expectThrows; + +import android.content.ComponentName; + +import org.junit.Test; + +/** + * Run it as {@code atest FrameworksMockingServicesTests:OwnerShellDataTest} + */ +public final class OwnerShellDataTest { + + private static final int USER_ID = 007; + private static final int PARENT_USER_ID = 'M' + 'I' + 6; + private static final ComponentName ADMIN = new ComponentName("Bond", "James"); + + @Test + public void testForDeviceOwner_noAdmin() { + expectThrows(NullPointerException.class, + () -> OwnerShellData.forDeviceOwner(USER_ID, /* admin= */ null)); + } + + @Test + public void testForDeviceOwner_invalidUser() { + expectThrows(IllegalArgumentException.class, + () -> OwnerShellData.forDeviceOwner(USER_NULL, ADMIN)); + } + + @Test + public void testForDeviceOwner() { + OwnerShellData dto = OwnerShellData.forDeviceOwner(USER_ID, ADMIN); + + assertWithMessage("dto(%s).userId", dto).that(dto.userId).isEqualTo(USER_ID); + assertWithMessage("dto(%s).parentUserId", dto).that(dto.parentUserId) + .isEqualTo(USER_NULL); + assertWithMessage("dto(%s).admin", dto).that(dto.admin).isSameInstanceAs(ADMIN); + assertWithMessage("dto(%s).isDeviceOwner", dto).that(dto.isDeviceOwner).isTrue(); + assertWithMessage("dto(%s).isProfileOwner", dto).that(dto.isProfileOwner).isFalse(); + assertWithMessage("dto(%s).isManagedProfileOwner", dto).that(dto.isManagedProfileOwner) + .isFalse(); + assertWithMessage("dto(%s).isAffiliated", dto).that(dto.isAffiliated).isFalse(); + } + + @Test + public void testForUserProfileOwner_noAdmin() { + expectThrows(NullPointerException.class, + () -> OwnerShellData.forUserProfileOwner(USER_ID, /* admin= */ null)); + } + + @Test + public void testForUserProfileOwner_invalidUser() { + expectThrows(IllegalArgumentException.class, + () -> OwnerShellData.forUserProfileOwner(USER_NULL, ADMIN)); + } + + @Test + public void testForUserProfileOwner() { + OwnerShellData dto = OwnerShellData.forUserProfileOwner(USER_ID, ADMIN); + + assertWithMessage("dto(%s).userId", dto).that(dto.userId).isEqualTo(USER_ID); + assertWithMessage("dto(%s).parentUserId", dto).that(dto.parentUserId) + .isEqualTo(USER_NULL); + assertWithMessage("dto(%s).admin", dto).that(dto.admin).isSameInstanceAs(ADMIN); + assertWithMessage("dto(%s).isDeviceOwner", dto).that(dto.isDeviceOwner).isFalse(); + assertWithMessage("dto(%s).isProfileOwner", dto).that(dto.isProfileOwner).isTrue(); + assertWithMessage("dto(%s).isManagedProfileOwner", dto).that(dto.isManagedProfileOwner) + .isFalse(); + assertWithMessage("dto(%s).isAffiliated", dto).that(dto.isAffiliated).isFalse(); + } + + @Test + public void testForManagedProfileOwner_noAdmin() { + expectThrows(NullPointerException.class, + () -> OwnerShellData.forManagedProfileOwner(USER_ID, PARENT_USER_ID, null)); + } + + @Test + public void testForManagedProfileOwner_invalidUser() { + expectThrows(IllegalArgumentException.class, + () -> OwnerShellData.forManagedProfileOwner(USER_NULL, PARENT_USER_ID, ADMIN)); + } + + @Test + public void testForManagedProfileOwner_invalidParent() { + expectThrows(IllegalArgumentException.class, + () -> OwnerShellData.forManagedProfileOwner(USER_ID, USER_NULL, ADMIN)); + } + + @Test + public void testForManagedProfileOwner_parentOfItself() { + expectThrows(IllegalArgumentException.class, + () -> OwnerShellData.forManagedProfileOwner(USER_ID, USER_ID, ADMIN)); + } + + @Test + public void testForManagedProfileOwner() { + OwnerShellData dto = OwnerShellData.forManagedProfileOwner(USER_ID, PARENT_USER_ID, ADMIN); + + assertWithMessage("dto(%s).userId", dto).that(dto.userId).isEqualTo(USER_ID); + assertWithMessage("dto(%s).parentUserId", dto).that(dto.parentUserId) + .isEqualTo(PARENT_USER_ID); + assertWithMessage("dto(%s).admin", dto).that(dto.admin).isSameInstanceAs(ADMIN); + assertWithMessage("dto(%s).isDeviceOwner", dto).that(dto.isDeviceOwner).isFalse(); + assertWithMessage("dto(%s).isProfileOwner", dto).that(dto.isProfileOwner).isFalse(); + assertWithMessage("dto(%s).isManagedProfileOwner", dto).that(dto.isManagedProfileOwner) + .isTrue(); + assertWithMessage("dto(%s).isAffiliated", dto).that(dto.isAffiliated).isFalse(); + } +}