diff options
6 files changed, 179 insertions, 1 deletions
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index c9060c46e0a4..d3132d85ad2f 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -88,6 +88,7 @@ import android.telephony.data.ApnSetting; import android.util.ArraySet; import android.util.Log; +import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.BackgroundThread; import com.android.internal.util.Preconditions; @@ -11207,6 +11208,40 @@ public class DevicePolicyManager { } /** + * Returns the combined set of the following: + * <ul> + * <li>The package names that the admin has previously set as allowed to request user consent + * for cross-profile communication, via {@link + * #setCrossProfilePackages(ComponentName, Set)}.</li> + * <li>The default package names set by the OEM that are allowed to request user consent for + * cross-profile communication without being explicitly enabled by the admin, via + * {@link R.array#cross_profile_apps}</li> + * </ul> + * + * @return the combined set of whitelisted package names set via + * {@link #setCrossProfilePackages(ComponentName, Set)} and + * {@link R.array#cross_profile_apps} + * + * @hide + */ + @RequiresPermission(anyOf = { + permission.INTERACT_ACROSS_USERS_FULL, + permission.INTERACT_ACROSS_USERS, + permission.INTERACT_ACROSS_PROFILES + }) + public @NonNull Set<String> getAllCrossProfilePackages() { + throwIfParentInstance("getDefaultCrossProfilePackages"); + if (mService != null) { + try { + return new ArraySet<>(mService.getAllCrossProfilePackages()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + return Collections.emptySet(); + } + + /** * Returns whether the device is being used as a managed kiosk. These requirements are as * follows: * <ul> diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 9c82ff6f51ac..08c5dff97884 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -444,6 +444,8 @@ interface IDevicePolicyManager { void setCrossProfilePackages(in ComponentName admin, in List<String> packageNames); List<String> getCrossProfilePackages(in ComponentName admin); + List<String> getAllCrossProfilePackages(); + boolean isManagedKiosk(); boolean isUnattendedManagedKiosk(); diff --git a/core/res/res/values/cross_profile_apps.xml b/core/res/res/values/cross_profile_apps.xml new file mode 100644 index 000000000000..ab6f20db0694 --- /dev/null +++ b/core/res/res/values/cross_profile_apps.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<resources> + <!-- + A collection of apps that have been pre-approved for cross-profile communication. + These will not require admin consent, but will still require user consent during provisioning. + --> + <string-array translatable="false" name="cross_profile_apps"> + </string-array> +</resources> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index c249458bd6fe..ccc0ad82809e 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1258,6 +1258,7 @@ <java-symbol type="array" name="vendor_disallowed_apps_managed_user" /> <java-symbol type="array" name="vendor_disallowed_apps_managed_profile" /> <java-symbol type="array" name="vendor_disallowed_apps_managed_device" /> + <java-symbol type="array" name="cross_profile_apps" /> <java-symbol type="drawable" name="default_wallpaper" /> <java-symbol type="drawable" name="default_lock_wallpaper" /> diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index c275ccc17146..881ab8aa805e 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -8524,7 +8524,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature) { return null; } - synchronized (getLockObject()) { return mOwners.getProfileOwnerComponent(userHandle); } @@ -8819,6 +8818,26 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } + private void enforceAcrossUsersPermissions() { + if (isCallerWithSystemUid() || mInjector.binderGetCallingUid() == Process.ROOT_UID) { + return; + } + if (mContext.checkCallingPermission(permission.INTERACT_ACROSS_PROFILES) + == PackageManager.PERMISSION_GRANTED) { + return; + } + if (mContext.checkCallingPermission(permission.INTERACT_ACROSS_USERS) + == PackageManager.PERMISSION_GRANTED) { + return; + } + if (mContext.checkCallingPermission(permission.INTERACT_ACROSS_USERS_FULL) + == PackageManager.PERMISSION_GRANTED) { + return; + } + throw new SecurityException("Calling user does not have INTERACT_ACROSS_PROFILES or" + + "INTERACT_ACROSS_USERS or INTERACT_ACROSS_USERS_FULL permissions"); + } + private void enforceFullCrossUsersPermission(int userHandle) { enforceSystemUserOrPermissionIfCrossUser(userHandle, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); @@ -14496,6 +14515,51 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } @Override + public List<String> getAllCrossProfilePackages() { + if (!mHasFeature) { + return Collections.emptyList(); + } + enforceAcrossUsersPermissions(); + + synchronized (getLockObject()) { + final List<ActiveAdmin> admins = getProfileOwnerAdminsForCurrentProfileGroup(); + final List<String> packages = getCrossProfilePackagesForAdmins(admins); + + packages.addAll(getDefaultCrossProfilePackages()); + + return packages; + } + } + + private List<String> getCrossProfilePackagesForAdmins(List<ActiveAdmin> admins) { + final List<String> packages = new ArrayList<>(); + for (int i = 0; i < admins.size(); i++) { + packages.addAll(admins.get(i).mCrossProfilePackages); + } + return packages; + } + + private List<String> getDefaultCrossProfilePackages() { + return Arrays.asList(mContext.getResources() + .getStringArray(R.array.cross_profile_apps)); + } + + private List<ActiveAdmin> getProfileOwnerAdminsForCurrentProfileGroup() { + synchronized (getLockObject()) { + final List<ActiveAdmin> admins = new ArrayList<>(); + int[] users = mUserManager.getProfileIdsWithDisabled(UserHandle.getCallingUserId()); + for (int i = 0; i < users.length; i++) { + final ComponentName componentName = getProfileOwner(users[i]); + if (componentName != null) { + admins.add(getActiveAdminForCallerLocked( + componentName, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER)); + } + } + return admins; + } + } + + @Override public boolean isManagedKiosk() { if (!mHasFeature) { return false; diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index 133d35da51dc..ee94dd698175 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -104,6 +104,7 @@ import com.android.server.devicepolicy.DevicePolicyManagerService.RestrictionsLi import org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.mockito.Mockito; +import org.mockito.internal.util.collections.Sets; import org.mockito.stubbing.Answer; import java.io.File; @@ -5570,6 +5571,57 @@ public class DevicePolicyManagerTest extends DpmTestBase { assertEquals(packages, dpm.getCrossProfilePackages(admin1)); } + public void testGetAllCrossProfilePackages_notSet_returnsEmpty() throws Exception { + addManagedProfile(admin1, mServiceContext.binder.callingUid, admin1); + + setCrossProfileAppsList(); + + assertTrue(dpm.getAllCrossProfilePackages().isEmpty()); + } + + public void testGetAllCrossProfilePackages_notSet_dpmsReinitialized_returnsEmpty() + throws Exception { + addManagedProfile(admin1, mServiceContext.binder.callingUid, admin1); + + setCrossProfileAppsList(); + initializeDpms(); + + assertTrue(dpm.getAllCrossProfilePackages().isEmpty()); + } + + public void testGetAllCrossProfilePackages_whenSet_returnsCombinedSet() throws Exception { + addManagedProfile(admin1, mServiceContext.binder.callingUid, admin1); + final Set<String> packages = Sets.newSet("TEST_PACKAGE", "TEST_COMMON_PACKAGE"); + + dpm.setCrossProfilePackages(admin1, packages); + setCrossProfileAppsList("TEST_DEFAULT_PACKAGE", "TEST_COMMON_PACKAGE"); + + assertEquals(Sets.newSet( + "TEST_PACKAGE", "TEST_DEFAULT_PACKAGE", "TEST_COMMON_PACKAGE"), + dpm.getAllCrossProfilePackages()); + + } + + public void testGetAllCrossProfilePackages_whenSet_dpmsReinitialized_returnsCombinedSet() + throws Exception { + addManagedProfile(admin1, mServiceContext.binder.callingUid, admin1); + final Set<String> packages = Sets.newSet("TEST_PACKAGE", "TEST_COMMON_PACKAGE"); + + dpm.setCrossProfilePackages(admin1, packages); + setCrossProfileAppsList("TEST_DEFAULT_PACKAGE", "TEST_COMMON_PACKAGE"); + initializeDpms(); + + assertEquals(Sets.newSet( + "TEST_PACKAGE", "TEST_DEFAULT_PACKAGE", "TEST_COMMON_PACKAGE"), + dpm.getAllCrossProfilePackages()); + } + + private void setCrossProfileAppsList(String... packages) { + when(mContext.getResources() + .getStringArray(eq(R.array.cross_profile_apps))) + .thenReturn(packages); + } + // admin1 is the outgoing DPC, adminAnotherPakcage is the incoming one. private void assertDeviceOwnershipRevertedWithFakeTransferMetadata() throws Exception { writeFakeTransferMetadataFile(UserHandle.USER_SYSTEM, |