diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java index ae253f5643..85cca6930e 100644 --- a/service/src/com/android/server/ConnectivityService.java +++ b/service/src/com/android/server/ConnectivityService.java @@ -108,6 +108,7 @@ import android.annotation.TargetApi; import android.app.AppOpsManager; import android.app.BroadcastOptions; import android.app.PendingIntent; +import android.app.admin.DevicePolicyManager; import android.app.usage.NetworkStatsManager; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -10635,13 +10636,29 @@ public class ConnectivityService extends IConnectivityManager.Stub mQosCallbackTracker.unregisterCallback(callback); } + private boolean isNetworkPreferenceAllowedForProfile(@NonNull UserHandle profile) { + // UserManager.isManagedProfile returns true for all apps in managed user profiles. + // Enterprise device can be fully managed like device owner and such use case + // also should be supported. Calling app check for work profile and fully managed device + // is already done in DevicePolicyManager. + // This check is an extra caution to be sure device is fully managed or not. + final UserManager um = mContext.getSystemService(UserManager.class); + final DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class); + if (um.isManagedProfile(profile.getIdentifier())) { + return true; + } + if (SdkLevel.isAtLeastT() && dpm.getDeviceOwner() != null) return true; + return false; + } + /** - * Request that a user profile is put by default on a network matching a given preference. + * Set a list of default network selection policies for a user profile or device owner. * * See the documentation for the individual preferences for a description of the supported * behaviors. * - * @param profile the user profile for whih the preference is being set. + * @param profile If the device owner is set, any profile is allowed. + Otherwise, the given profile can only be managed profile. * @param preferences the list of profile network preferences for the * provided profile. * @param listener an optional listener to listen for completion of the operation. @@ -10666,9 +10683,9 @@ public class ConnectivityService extends IConnectivityManager.Stub throw new IllegalArgumentException("Must explicitly specify a user handle (" + "UserHandle.CURRENT not supported)"); } - final UserManager um = mContext.getSystemService(UserManager.class); - if (!um.isManagedProfile(profile.getIdentifier())) { - throw new IllegalArgumentException("Profile must be a managed profile"); + if (!isNetworkPreferenceAllowedForProfile(profile)) { + throw new IllegalArgumentException("Profile must be a managed profile " + + "or the device owner must be set. "); } final List preferenceList = diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java index 37df4eb1ab..cd239d5d04 100644 --- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java @@ -194,6 +194,7 @@ import android.app.AlarmManager; import android.app.AppOpsManager; import android.app.NotificationManager; import android.app.PendingIntent; +import android.app.admin.DevicePolicyManager; import android.app.usage.NetworkStatsManager; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -540,6 +541,7 @@ public class ConnectivityServiceTest { @Mock NetworkPolicyManager mNetworkPolicyManager; @Mock VpnProfileStore mVpnProfileStore; @Mock SystemConfigManager mSystemConfigManager; + @Mock DevicePolicyManager mDevicePolicyManager; @Mock Resources mResources; @Mock ClatCoordinator mClatCoordinator; @Mock PacProxyManager mPacProxyManager; @@ -661,6 +663,7 @@ public class ConnectivityServiceTest { if (Context.TELEPHONY_SERVICE.equals(name)) return mTelephonyManager; if (Context.ETHERNET_SERVICE.equals(name)) return mEthernetManager; if (Context.NETWORK_POLICY_SERVICE.equals(name)) return mNetworkPolicyManager; + if (Context.DEVICE_POLICY_SERVICE.equals(name)) return mDevicePolicyManager; if (Context.SYSTEM_CONFIG_SERVICE.equals(name)) return mSystemConfigManager; if (Context.NETWORK_STATS_SERVICE.equals(name)) return mStatsManager; if (Context.BATTERY_STATS_SERVICE.equals(name)) return mBatteryStatsManager; @@ -689,6 +692,14 @@ public class ConnectivityServiceTest { doReturn(value).when(mUserManager).isManagedProfile(eq(userHandle.getIdentifier())); } + public void setDeviceOwner(@NonNull final UserHandle userHandle, String value) { + // This relies on all contexts for a given user returning the same UM mock + final DevicePolicyManager dpmMock = createContextAsUser(userHandle, 0 /* flags */) + .getSystemService(DevicePolicyManager.class); + doReturn(value).when(dpmMock).getDeviceOwner(); + doReturn(value).when(mDevicePolicyManager).getDeviceOwner(); + } + @Override public ContentResolver getContentResolver() { return mContentResolver; @@ -14729,12 +14740,41 @@ public class ConnectivityServiceTest { public void testProfileNetworkPrefWrongProfile() throws Exception { final UserHandle testHandle = UserHandle.of(TEST_WORK_PROFILE_USER_ID); mServiceContext.setWorkProfile(testHandle, false); - assertThrows("Should not be able to set a user pref for a non-work profile", + mServiceContext.setDeviceOwner(testHandle, null); + assertThrows("Should not be able to set a user pref for a non-work profile " + + "and non device owner", IllegalArgumentException.class , () -> mCm.setProfileNetworkPreference(testHandle, PROFILE_NETWORK_PREFERENCE_ENTERPRISE, null, null)); } + /** + * Make sure requests for per-profile default networking for a device owner is + * accepted on T and not accepted on S + */ + public void testProfileNetworkDeviceOwner() throws Exception { + final UserHandle testHandle = UserHandle.of(TEST_WORK_PROFILE_USER_ID); + mServiceContext.setWorkProfile(testHandle, false); + mServiceContext.setDeviceOwner(testHandle, "deviceOwnerPackage"); + ProfileNetworkPreference.Builder profileNetworkPreferenceBuilder = + new ProfileNetworkPreference.Builder(); + profileNetworkPreferenceBuilder.setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE); + profileNetworkPreferenceBuilder.setPreferenceEnterpriseId(NET_ENTERPRISE_ID_1); + final TestOnCompleteListener listener = new TestOnCompleteListener(); + if (SdkLevel.isAtLeastT()) { + mCm.setProfileNetworkPreferences(testHandle, + List.of(profileNetworkPreferenceBuilder.build()), + r -> r.run(), listener); + } else { + // S should not allow setting preference on device owner + assertThrows("Should not be able to set a user pref for a non-work profile on S", + IllegalArgumentException.class , () -> + mCm.setProfileNetworkPreferences(testHandle, + List.of(profileNetworkPreferenceBuilder.build()), + r -> r.run(), listener)); + } + } + @Test public void testSubIdsClearedWithoutNetworkFactoryPermission() throws Exception { mServiceContext.setPermission(NETWORK_FACTORY, PERMISSION_DENIED);