diff --git a/core/api/current.txt b/core/api/current.txt index ab2624c83998e..b95cb6a8b37ac 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -7832,6 +7832,7 @@ package android.app.admin { method @Deprecated public void setCrossProfileCallerIdDisabled(@NonNull android.content.ComponentName, boolean); method @Deprecated public void setCrossProfileContactsSearchDisabled(@NonNull android.content.ComponentName, boolean); method public void setCrossProfilePackages(@NonNull android.content.ComponentName, @NonNull java.util.Set); + method public void setDefaultDialerApplication(@NonNull String); method public void setDefaultSmsApplication(@NonNull android.content.ComponentName, @NonNull String); method public void setDelegatedScopes(@NonNull android.content.ComponentName, @NonNull String, @NonNull java.util.List); method public void setDeviceOwnerLockScreenInfo(@NonNull android.content.ComponentName, CharSequence); diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index e7f6990fb4953..2e2638766745d 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -9730,6 +9730,11 @@ public class DevicePolicyManager { * of an organization-owned managed profile and the package must be a pre-installed system * package. If called on the parent instance, then the default SMS application is set on the * personal profile. + *

+ * Starting from Android {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, the profile + * owner of an organization-owned managed profile can also call this method directly (not on the + * parent profile instance) to set the default SMS application in the work profile. This is only + * meaningful when work profile telephony is enabled by {@link #setManagedSubscriptionsPolicy}. * * @param admin Which {@link DeviceAdminReceiver} this request is associated with. * @param packageName The name of the package to set as the default SMS application. @@ -9750,6 +9755,36 @@ public class DevicePolicyManager { } } + /** + * Must be called by a device owner or a profile owner of an organization-owned managed profile + * to set the default dialer application for the calling user. + *

+ * When the profile owner of an organization-owned managed profile calls this method, it sets + * the default dialer application in the work profile. This is only meaningful when work profile + * telephony is enabled by {@link #setManagedSubscriptionsPolicy}. + *

+ * If the device does not support telephony ({@link PackageManager#FEATURE_TELEPHONY}), calling + * this method will do nothing. + * + * @param packageName The name of the package to set as the default dialer application. + * @throws SecurityException if {@code admin} is not a device or profile owner or a + * profile owner of an organization-owned managed profile. + * @throws IllegalArgumentException if the package cannot be set as the default dialer, for + * example if the package is not installed or does not expose + * the expected activities or services that a dialer app is + * required to have. + */ + public void setDefaultDialerApplication(@NonNull String packageName) { + throwIfParentInstance("setDefaultDialerApplication"); + if (mService != null) { + try { + mService.setDefaultDialerApplication(packageName); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + /** * Called by a profile owner or device owner to grant permission to a package to manage * application restrictions for the calling user via {@link #setApplicationRestrictions} and diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index c86852a9c6153..182dc2f093247 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -237,6 +237,7 @@ interface IDevicePolicyManager { void clearPackagePersistentPreferredActivities(in ComponentName admin, String packageName); void setDefaultSmsApplication(in ComponentName admin, String packageName, boolean parent); + void setDefaultDialerApplication(String packageName); void setApplicationRestrictions(in ComponentName who, in String callerPackage, in String packageName, in Bundle settings); Bundle getApplicationRestrictions(in ComponentName who, in String callerPackage, in String packageName); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java index d7a20953e1564..272ad5f19f49e 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java @@ -175,6 +175,8 @@ class ActiveAdmin { private static final String ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS = "num-notifications"; private static final String ATTR_PACKAGE_POLICY_MODE = "package-policy-type"; private static final String TAG_CREDENTIAL_MANAGER_POLICY = "credential-manager-policy"; + private static final String TAG_DIALER_PACKAGE = "dialer_package"; + private static final String TAG_SMS_PACKAGE = "sms_package"; DeviceAdminInfo info; @@ -349,6 +351,8 @@ class ActiveAdmin { boolean mUsbDataSignalingEnabled = USB_DATA_SIGNALING_ENABLED_DEFAULT; int mWifiMinimumSecurityLevel = DevicePolicyManager.WIFI_SECURITY_OPEN; + String mDialerPackage; + String mSmsPackage; ActiveAdmin(DeviceAdminInfo info, boolean isParent) { this.info = info; @@ -658,6 +662,12 @@ class ActiveAdmin { mManagedSubscriptionsPolicy.saveToXml(out); out.endTag(null, TAG_MANAGED_SUBSCRIPTIONS_POLICY); } + if (!TextUtils.isEmpty(mDialerPackage)) { + writeAttributeValueToXml(out, TAG_DIALER_PACKAGE, mDialerPackage); + } + if (!TextUtils.isEmpty(mSmsPackage)) { + writeAttributeValueToXml(out, TAG_SMS_PACKAGE, mSmsPackage); + } } private void writePackagePolicy(TypedXmlSerializer out, String tag, @@ -966,6 +976,10 @@ class ActiveAdmin { mManagedSubscriptionsPolicy = ManagedSubscriptionsPolicy.readFromXml(parser); } else if (TAG_CREDENTIAL_MANAGER_POLICY.equals(tag)) { mCredentialManagerPolicy = readPackagePolicy(parser); + } else if (TAG_DIALER_PACKAGE.equals(tag)) { + mDialerPackage = parser.getAttributeValue(null, ATTR_VALUE); + } else if (TAG_SMS_PACKAGE.equals(tag)) { + mSmsPackage = parser.getAttributeValue(null, ATTR_VALUE); } else { Slogf.w(LOG_TAG, "Unknown admin tag: %s", tag); XmlUtils.skipCurrentTag(parser); @@ -1444,5 +1458,10 @@ class ActiveAdmin { pw.println(mManagedSubscriptionsPolicy); pw.decreaseIndent(); } + + pw.print("mDialerPackage="); + pw.println(mDialerPackage); + pw.print("mSmsPackage="); + pw.println(mSmsPackage); } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index f5a1b80c9b966..d5a1b75b9edda 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -313,6 +313,7 @@ import android.net.Uri; import android.net.VpnManager; import android.net.metrics.IpConnectivityLog; import android.net.wifi.WifiManager; +import android.os.AsyncTask; import android.os.Binder; import android.os.Build; import android.os.Bundle; @@ -453,8 +454,12 @@ import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -807,6 +812,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { final UserManagerInternal mUserManagerInternal; final UsageStatsManagerInternal mUsageStatsManagerInternal; final TelephonyManager mTelephonyManager; + final RoleManager mRoleManager; private final LockPatternUtils mLockPatternUtils; private final LockSettingsInternal mLockSettingsInternal; private final DeviceAdminServiceController mDeviceAdminServiceController; @@ -1593,6 +1599,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return mContext.getSystemService(TelephonyManager.class); } + RoleManager getRoleManager() { + return mContext.getSystemService(RoleManager.class); + } + TrustManager getTrustManager() { return (TrustManager) mContext.getSystemService(Context.TRUST_SERVICE); } @@ -1935,6 +1945,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { mIPackageManager = Objects.requireNonNull(injector.getIPackageManager()); mIPermissionManager = Objects.requireNonNull(injector.getIPermissionManager()); mTelephonyManager = Objects.requireNonNull(injector.getTelephonyManager()); + mRoleManager = Objects.requireNonNull(injector.getRoleManager()); mLocalService = new LocalService(); mLockPatternUtils = injector.newLockPatternUtils(); @@ -10488,14 +10499,73 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { final CallerIdentity caller = getCallerIdentity(admin); Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller) - || (parent && isProfileOwnerOfOrganizationOwnedDevice(caller))); + || isProfileOwnerOfOrganizationOwnedDevice(caller)); + final int userId; if (parent) { + userId = getProfileParentId(mInjector.userHandleGetCallingUserId()); mInjector.binderWithCleanCallingIdentity(() -> enforcePackageIsSystemPackage( - packageName, getProfileParentId(mInjector.userHandleGetCallingUserId()))); + packageName, userId)); + } else { + userId = mInjector.userHandleGetCallingUserId(); } mInjector.binderWithCleanCallingIdentity(() -> - SmsApplication.setDefaultApplication(packageName, mContext)); + SmsApplication.setDefaultApplicationAsUser(packageName, mContext, userId)); + + synchronized (getLockObject()) { + final ActiveAdmin activeAdmin = getParentOfAdminIfRequired( + getProfileOwnerOrDeviceOwnerLocked(caller.getUserId()), parent); + if (!Objects.equals(activeAdmin.mSmsPackage, packageName)) { + activeAdmin.mSmsPackage = packageName; + saveSettingsLocked(caller.getUserId()); + } + } + } + + @Override + public void setDefaultDialerApplication(String packageName) { + if (!mHasFeature || !mHasTelephonyFeature) { + return; + } + + final CallerIdentity caller = getCallerIdentity(); + final int callerUserId = caller.getUserId(); + Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller) + || isProfileOwnerOfOrganizationOwnedDevice(caller)); + mInjector.binderWithCleanCallingIdentity(() -> { + CompletableFuture future = new CompletableFuture<>(); + Consumer callback = successful -> { + if (successful) { + future.complete(null); + } else { + future.completeExceptionally(new IllegalArgumentException( + packageName + " cannot be set as the dialer")); + } + }; + mRoleManager.addRoleHolderAsUser( + RoleManager.ROLE_DIALER, packageName, 0, UserHandle.of(callerUserId), + AsyncTask.THREAD_POOL_EXECUTOR, callback); + try { + future.get(5, TimeUnit.SECONDS); + } catch (TimeoutException e) { + throw new IllegalArgumentException("Timeout when setting the app as the dialer", e); + } catch (ExecutionException e) { + Throwable cause = e.getCause(); + if (cause instanceof IllegalArgumentException) { + throw (IllegalArgumentException) cause; + } else { + throw new IllegalStateException(cause); + } + } + }); + // Only save the package when the setting the role succeeded without exception. + synchronized (getLockObject()) { + final ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(callerUserId); + if (!Objects.equals(admin.mDialerPackage, packageName)) { + admin.mDialerPackage = packageName; + saveSettingsLocked(callerUserId); + } + } } @Override