diff options
| author | 2022-04-27 10:27:02 +0000 | |
|---|---|---|
| committer | 2022-04-27 10:27:02 +0000 | |
| commit | e6474ba2a113ded8f70cf4e8aa672a96207f6453 (patch) | |
| tree | ccfd817a4c315a1f6977b4a6d974776d375d1c19 | |
| parent | 51ef29cc0617ffe80e656d41e8c23388a7ab8c6d (diff) | |
| parent | 747806ddf87a0be6961643d12b0f0148a0ea71a0 (diff) | |
Merge "Change bypassRoleQualification persistence logic" into tm-dev am: 747806ddf8
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/17539065
Change-Id: I1582d22f1bef65118bd475e6a62cd95df76ac2c0
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
5 files changed, 163 insertions, 9 deletions
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java index 9a0b5c7ef5ae..48a436f15803 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java @@ -69,6 +69,7 @@ class DevicePolicyData { private static final String TAG_PASSWORD_VALIDITY = "password-validity"; private static final String TAG_PASSWORD_TOKEN_HANDLE = "password-token"; private static final String TAG_PROTECTED_PACKAGES = "protected-packages"; + private static final String TAG_BYPASS_ROLE_QUALIFICATIONS = "bypass-role-qualifications"; private static final String ATTR_VALUE = "value"; private static final String ATTR_ALIAS = "alias"; private static final String ATTR_ID = "id"; @@ -107,6 +108,8 @@ class DevicePolicyData { int mPasswordOwner = -1; long mLastMaximumTimeToLock = -1; boolean mUserSetupComplete = false; + boolean mBypassDevicePolicyManagementRoleQualifications = false; + String mCurrentRoleHolder; boolean mPaired = false; int mUserProvisioningState; int mPermissionPolicy; @@ -374,6 +377,12 @@ class DevicePolicyData { out.endTag(null, TAG_APPS_SUSPENDED); } + if (policyData.mBypassDevicePolicyManagementRoleQualifications) { + out.startTag(null, TAG_BYPASS_ROLE_QUALIFICATIONS); + out.attribute(null, ATTR_VALUE, policyData.mCurrentRoleHolder); + out.endTag(null, TAG_BYPASS_ROLE_QUALIFICATIONS); + } + out.endTag(null, "policies"); out.endDocument(); @@ -558,6 +567,9 @@ class DevicePolicyData { } else if (TAG_APPS_SUSPENDED.equals(tag)) { policy.mAppsSuspended = parser.getAttributeBoolean(null, ATTR_VALUE, false); + } else if (TAG_BYPASS_ROLE_QUALIFICATIONS.equals(tag)) { + policy.mBypassDevicePolicyManagementRoleQualifications = true; + policy.mCurrentRoleHolder = parser.getAttributeValue(null, ATTR_VALUE); } else { Slogf.w(TAG, "Unknown tag: %s", tag); XmlUtils.skipCurrentTag(parser); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index e2d8a63fbfda..d1fac871fa2e 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -138,7 +138,6 @@ import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_DEFAULT import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE; import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK; import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK; -import static android.provider.Settings.Global.BYPASS_DEVICE_POLICY_MANAGEMENT_ROLE_QUALIFICATIONS; import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER; import static android.provider.Settings.Secure.MANAGED_PROVISIONING_DPC_DOWNLOADED; import static android.provider.Settings.Secure.USER_SETUP_COMPLETE; @@ -222,6 +221,7 @@ import android.app.admin.UnsafeStateException; import android.app.admin.WifiSsidPolicy; import android.app.backup.IBackupManager; import android.app.compat.CompatChanges; +import android.app.role.OnRoleHoldersChangedListener; import android.app.role.RoleManager; import android.app.trust.TrustManager; import android.app.usage.UsageStatsManagerInternal; @@ -255,6 +255,7 @@ import android.content.pm.ParceledListSlice; import android.content.pm.PermissionInfo; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; +import android.content.pm.Signature; import android.content.pm.StringParceledListSlice; import android.content.pm.UserInfo; import android.content.res.Resources; @@ -409,6 +410,7 @@ import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import java.util.function.Function; import java.util.function.Predicate; @@ -756,6 +758,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { private @UserIdInt int mNetworkLoggingNotificationUserId = UserHandle.USER_NULL; private final DeviceManagementResourcesProvider mDeviceManagementResourcesProvider; + private final DevicePolicyManagementRoleObserver mDevicePolicyManagementRoleObserver; private static final boolean ENABLE_LOCK_GUARD = true; @@ -1823,6 +1826,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { mBugreportCollectionManager = new RemoteBugreportManager(this, mInjector); mDeviceManagementResourcesProvider = mInjector.getDeviceManagementResourcesProvider(); + mDevicePolicyManagementRoleObserver = new DevicePolicyManagementRoleObserver(mContext); + mDevicePolicyManagementRoleObserver.register(); // "Lite" interface is available even when the device doesn't have the feature LocalServices.addService(DevicePolicyManagerLiteInternal.class, mLocalService); @@ -18663,16 +18668,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Preconditions.checkCallAuthorization(hasCallingOrSelfPermission( android.Manifest.permission.MANAGE_ROLE_HOLDERS)); return mInjector.binderWithCleanCallingIdentity(() -> { - if (mInjector.settingsGlobalGetInt( - BYPASS_DEVICE_POLICY_MANAGEMENT_ROLE_QUALIFICATIONS, /* def= */ 0) == 1) { + if (getUserData( + UserHandle.USER_SYSTEM).mBypassDevicePolicyManagementRoleQualifications) { return true; } - if (shouldAllowBypassingDevicePolicyManagementRoleQualificationInternal()) { - mInjector.settingsGlobalPutInt( - BYPASS_DEVICE_POLICY_MANAGEMENT_ROLE_QUALIFICATIONS, /* value= */ 1); - return true; - } - return false; + return shouldAllowBypassingDevicePolicyManagementRoleQualificationInternal(); }); } @@ -18685,6 +18685,142 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return accounts.length == 0; } + private void setBypassDevicePolicyManagementRoleQualificationStateInternal( + String currentRoleHolder, boolean allowBypass) { + boolean stateChanged = false; + DevicePolicyData policy = getUserData(UserHandle.USER_SYSTEM); + if (policy.mBypassDevicePolicyManagementRoleQualifications != allowBypass) { + policy.mBypassDevicePolicyManagementRoleQualifications = allowBypass; + stateChanged = true; + } + if (!Objects.equals(currentRoleHolder, policy.mCurrentRoleHolder)) { + policy.mCurrentRoleHolder = currentRoleHolder; + stateChanged = true; + } + if (stateChanged) { + synchronized (getLockObject()) { + saveSettingsLocked(UserHandle.USER_SYSTEM); + } + } + } + + private final class DevicePolicyManagementRoleObserver implements OnRoleHoldersChangedListener { + private RoleManager mRm; + private final Executor mExecutor; + private final Context mContext; + + DevicePolicyManagementRoleObserver(@NonNull Context context) { + mContext = context; + mExecutor = mContext.getMainExecutor(); + mRm = mContext.getSystemService(RoleManager.class); + } + + public void register() { + mRm.addOnRoleHoldersChangedListenerAsUser(mExecutor, this, UserHandle.SYSTEM); + } + + @Override + public void onRoleHoldersChanged(@NonNull String roleName, @NonNull UserHandle user) { + if (!RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT.equals(roleName)) { + return; + } + String newRoleHolder = getRoleHolder(); + if (isDefaultRoleHolder(newRoleHolder)) { + Slogf.i(LOG_TAG, + "onRoleHoldersChanged: Default role holder is set, returning early"); + return; + } + if (newRoleHolder == null) { + Slogf.i(LOG_TAG, + "onRoleHoldersChanged: New role holder is null, returning early"); + return; + } + if (shouldAllowBypassingDevicePolicyManagementRoleQualificationInternal()) { + Slogf.w(LOG_TAG, + "onRoleHoldersChanged: Updating current role holder to " + newRoleHolder); + setBypassDevicePolicyManagementRoleQualificationStateInternal( + newRoleHolder, /* allowBypass= */ true); + return; + } + DevicePolicyData policy = getUserData(UserHandle.USER_SYSTEM); + if (!newRoleHolder.equals(policy.mCurrentRoleHolder)) { + Slogf.w(LOG_TAG, + "onRoleHoldersChanged: You can't set a different role holder, role " + + "is getting revoked from " + newRoleHolder); + setBypassDevicePolicyManagementRoleQualificationStateInternal( + /* currentRoleHolder= */ null, /* allowBypass= */ false); + mRm.removeRoleHolderAsUser( + RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT, + newRoleHolder, + /* flags= */ 0, + user, + mExecutor, + successful -> {}); + } + } + + private String getRoleHolder() { + return DevicePolicyManagerService.this.getDevicePolicyManagementRoleHolderPackageName( + mContext); + } + + private boolean isDefaultRoleHolder(String packageName) { + String defaultRoleHolder = getDefaultRoleHolderPackageName(); + if (packageName == null || defaultRoleHolder == null) { + return false; + } + if (!defaultRoleHolder.equals(packageName)) { + return false; + } + return hasSigningCertificate( + packageName, getDefaultRoleHolderPackageSignature()); + } + + private boolean hasSigningCertificate(String packageName, String certificateString) { + if (packageName == null || certificateString == null) { + return false; + } + byte[] certificate; + try { + certificate = new Signature(certificateString).toByteArray(); + } catch (IllegalArgumentException e) { + Slogf.w(LOG_TAG, "Cannot parse signing certificate: " + certificateString, e); + return false; + } + PackageManager pm = mInjector.getPackageManager(); + return pm.hasSigningCertificate( + packageName, certificate, PackageManager.CERT_INPUT_SHA256); + } + + private String getDefaultRoleHolderPackageName() { + String[] info = getDefaultRoleHolderPackageNameAndSignature(); + if (info == null) { + return null; + } + return info[0]; + } + + private String getDefaultRoleHolderPackageSignature() { + String[] info = getDefaultRoleHolderPackageNameAndSignature(); + if (info == null || info.length < 2) { + return null; + } + return info[1]; + } + + private String[] getDefaultRoleHolderPackageNameAndSignature() { + String packageNameAndSignature = mContext.getString( + com.android.internal.R.string.config_devicePolicyManagement); + if (TextUtils.isEmpty(packageNameAndSignature)) { + return null; + } + if (packageNameAndSignature.contains(":")) { + return packageNameAndSignature.split(":"); + } + return new String[]{packageNameAndSignature}; + } + } + @Override public List<UserHandle> getPolicyManagedProfiles(@NonNull UserHandle user) { Preconditions.checkCallAuthorization(hasCallingOrSelfPermission( diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml index 449177ef9b7d..0afb1829f9d4 100644 --- a/services/tests/servicestests/AndroidManifest.xml +++ b/services/tests/servicestests/AndroidManifest.xml @@ -102,6 +102,7 @@ <uses-permission android:name="android.permission.READ_NEARBY_STREAMING_POLICY" /> <uses-permission android:name="android.permission.MODIFY_AUDIO_ROUTING" /> <uses-permission android:name="android.permission.PACKAGE_VERIFICATION_AGENT" /> + <uses-permission android:name="android.permission.OBSERVE_ROLE_HOLDERS" /> <queries> <package android:name="com.android.servicestests.apps.suspendtestapp" /> diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java index 2cf67f83578b..e991ec6879ae 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java @@ -237,6 +237,8 @@ public class DpmMockContext extends MockContext { return mMockSystemServices.devicePolicyManager; case Context.LOCATION_SERVICE: return mMockSystemServices.locationManager; + case Context.ROLE_SERVICE: + return mMockSystemServices.roleManager; } throw new UnsupportedOperationException(); } diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java index 34c9f7c2ef87..884ffce155d7 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java @@ -33,6 +33,7 @@ import android.app.IActivityTaskManager; import android.app.NotificationManager; import android.app.admin.DevicePolicyManager; import android.app.backup.IBackupManager; +import android.app.role.RoleManager; import android.app.usage.UsageStatsManagerInternal; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -131,6 +132,7 @@ public class MockSystemServices { public final VpnManager vpnManager; public final DevicePolicyManager devicePolicyManager; public final LocationManager locationManager; + public final RoleManager roleManager; /** Note this is a partial mock, not a real mock. */ public final PackageManager packageManager; public final BuildMock buildMock = new BuildMock(); @@ -181,6 +183,7 @@ public class MockSystemServices { vpnManager = mock(VpnManager.class); devicePolicyManager = mock(DevicePolicyManager.class); locationManager = mock(LocationManager.class); + roleManager = realContext.getSystemService(RoleManager.class); // Package manager is huge, so we use a partial mock instead. packageManager = spy(realContext.getPackageManager()); |