Merge "Enforce carrier privileges when setting/clearing VCN configs"
This commit is contained in:
@@ -25,13 +25,22 @@ import android.net.NetworkProvider;
|
|||||||
import android.net.NetworkRequest;
|
import android.net.NetworkRequest;
|
||||||
import android.net.vcn.IVcnManagementService;
|
import android.net.vcn.IVcnManagementService;
|
||||||
import android.net.vcn.VcnConfig;
|
import android.net.vcn.VcnConfig;
|
||||||
|
import android.os.Binder;
|
||||||
import android.os.HandlerThread;
|
import android.os.HandlerThread;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.os.ParcelUuid;
|
import android.os.ParcelUuid;
|
||||||
|
import android.os.Process;
|
||||||
|
import android.os.UserHandle;
|
||||||
|
import android.telephony.SubscriptionInfo;
|
||||||
|
import android.telephony.SubscriptionManager;
|
||||||
|
import android.telephony.TelephonyManager;
|
||||||
|
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.internal.annotations.VisibleForTesting.Visibility;
|
import com.android.internal.annotations.VisibleForTesting.Visibility;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* VcnManagementService manages Virtual Carrier Network profiles and lifecycles.
|
* VcnManagementService manages Virtual Carrier Network profiles and lifecycles.
|
||||||
*
|
*
|
||||||
@@ -130,6 +139,18 @@ public class VcnManagementService extends IVcnManagementService.Stub {
|
|||||||
}
|
}
|
||||||
return mHandlerThread.getLooper();
|
return mHandlerThread.getLooper();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the caller's UID
|
||||||
|
*
|
||||||
|
* <p>This call MUST be made before calling {@link Binder#clearCallingIdentity}, otherwise
|
||||||
|
* this will not work properly.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public int getBinderCallingUid() {
|
||||||
|
return Binder.getCallingUid();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Notifies the VcnManagementService that external dependencies can be set up. */
|
/** Notifies the VcnManagementService that external dependencies can be set up. */
|
||||||
@@ -140,6 +161,50 @@ public class VcnManagementService extends IVcnManagementService.Stub {
|
|||||||
.registerNetworkProvider(mNetworkProvider);
|
.registerNetworkProvider(mNetworkProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void enforcePrimaryUser() {
|
||||||
|
final int uid = mDeps.getBinderCallingUid();
|
||||||
|
if (uid == Process.SYSTEM_UID) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Calling identity was System Server. Was Binder calling identity cleared?");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!UserHandle.getUserHandleForUid(uid).isSystem()) {
|
||||||
|
throw new SecurityException(
|
||||||
|
"VcnManagementService can only be used by callers running as the primary user");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void enforceCallingUserAndCarrierPrivilege(ParcelUuid subscriptionGroup) {
|
||||||
|
// Only apps running in the primary (system) user are allowed to configure the VCN. This is
|
||||||
|
// in line with Telephony's behavior with regards to binding to a Carrier App provided
|
||||||
|
// CarrierConfigService.
|
||||||
|
enforcePrimaryUser();
|
||||||
|
|
||||||
|
// TODO (b/172619301): Check based on events propagated from CarrierPrivilegesTracker
|
||||||
|
final SubscriptionManager subMgr = mContext.getSystemService(SubscriptionManager.class);
|
||||||
|
final List<SubscriptionInfo> subscriptionInfos = new ArrayList<>();
|
||||||
|
Binder.withCleanCallingIdentity(
|
||||||
|
() -> {
|
||||||
|
subscriptionInfos.addAll(subMgr.getSubscriptionsInGroup(subscriptionGroup));
|
||||||
|
});
|
||||||
|
|
||||||
|
final TelephonyManager telMgr = mContext.getSystemService(TelephonyManager.class);
|
||||||
|
for (SubscriptionInfo info : subscriptionInfos) {
|
||||||
|
// Check subscription is active first; much cheaper/faster check, and an app (currently)
|
||||||
|
// cannot be carrier privileged for inactive subscriptions.
|
||||||
|
if (subMgr.isValidSlotIndex(info.getSimSlotIndex())
|
||||||
|
&& telMgr.hasCarrierPrivileges(info.getSubscriptionId())) {
|
||||||
|
// TODO (b/173717728): Allow configuration for inactive, but manageable
|
||||||
|
// subscriptions.
|
||||||
|
// TODO (b/173718661): Check for whole subscription groups at a time.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new SecurityException(
|
||||||
|
"Carrier privilege required for subscription group to set VCN Config");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets a VCN config for a given subscription group.
|
* Sets a VCN config for a given subscription group.
|
||||||
*
|
*
|
||||||
@@ -150,6 +215,10 @@ public class VcnManagementService extends IVcnManagementService.Stub {
|
|||||||
requireNonNull(subscriptionGroup, "subscriptionGroup was null");
|
requireNonNull(subscriptionGroup, "subscriptionGroup was null");
|
||||||
requireNonNull(config, "config was null");
|
requireNonNull(config, "config was null");
|
||||||
|
|
||||||
|
enforceCallingUserAndCarrierPrivilege(subscriptionGroup);
|
||||||
|
|
||||||
|
// TODO: Clear Binder calling identity
|
||||||
|
|
||||||
// TODO: Store VCN configuration, trigger startup as necessary
|
// TODO: Store VCN configuration, trigger startup as necessary
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,6 +231,10 @@ public class VcnManagementService extends IVcnManagementService.Stub {
|
|||||||
public void clearVcnConfig(@NonNull ParcelUuid subscriptionGroup) {
|
public void clearVcnConfig(@NonNull ParcelUuid subscriptionGroup) {
|
||||||
requireNonNull(subscriptionGroup, "subscriptionGroup was null");
|
requireNonNull(subscriptionGroup, "subscriptionGroup was null");
|
||||||
|
|
||||||
|
enforceCallingUserAndCarrierPrivilege(subscriptionGroup);
|
||||||
|
|
||||||
|
// TODO: Clear Binder calling identity
|
||||||
|
|
||||||
// TODO: Clear VCN configuration, trigger teardown as necessary
|
// TODO: Clear VCN configuration, trigger teardown as necessary
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,14 +16,23 @@
|
|||||||
|
|
||||||
package com.android.server;
|
package com.android.server;
|
||||||
|
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
import static org.mockito.Mockito.any;
|
import static org.mockito.Mockito.any;
|
||||||
import static org.mockito.Mockito.doReturn;
|
import static org.mockito.Mockito.doReturn;
|
||||||
|
import static org.mockito.Mockito.eq;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.net.ConnectivityManager;
|
import android.net.ConnectivityManager;
|
||||||
|
import android.net.vcn.VcnConfig;
|
||||||
|
import android.os.ParcelUuid;
|
||||||
|
import android.os.Process;
|
||||||
|
import android.os.UserHandle;
|
||||||
import android.os.test.TestLooper;
|
import android.os.test.TestLooper;
|
||||||
|
import android.telephony.SubscriptionInfo;
|
||||||
|
import android.telephony.SubscriptionManager;
|
||||||
|
import android.telephony.TelephonyManager;
|
||||||
|
|
||||||
import androidx.test.filters.SmallTest;
|
import androidx.test.filters.SmallTest;
|
||||||
import androidx.test.runner.AndroidJUnit4;
|
import androidx.test.runner.AndroidJUnit4;
|
||||||
@@ -31,27 +40,73 @@ import androidx.test.runner.AndroidJUnit4;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/** Tests for {@link VcnManagementService}. */
|
/** Tests for {@link VcnManagementService}. */
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
@SmallTest
|
@SmallTest
|
||||||
public class VcnManagementServiceTest {
|
public class VcnManagementServiceTest {
|
||||||
|
private static final ParcelUuid TEST_UUID_1 = new ParcelUuid(new UUID(0, 0));
|
||||||
|
private static final SubscriptionInfo TEST_SUBSCRIPTION_INFO =
|
||||||
|
new SubscriptionInfo(
|
||||||
|
1 /* id */,
|
||||||
|
"" /* iccId */,
|
||||||
|
0 /* simSlotIndex */,
|
||||||
|
"Carrier" /* displayName */,
|
||||||
|
"Carrier" /* carrierName */,
|
||||||
|
0 /* nameSource */,
|
||||||
|
255 /* iconTint */,
|
||||||
|
"12345" /* number */,
|
||||||
|
0 /* roaming */,
|
||||||
|
null /* icon */,
|
||||||
|
"0" /* mcc */,
|
||||||
|
"0" /* mnc */,
|
||||||
|
"0" /* countryIso */,
|
||||||
|
false /* isEmbedded */,
|
||||||
|
null /* nativeAccessRules */,
|
||||||
|
null /* cardString */,
|
||||||
|
false /* isOpportunistic */,
|
||||||
|
TEST_UUID_1.toString() /* groupUUID */,
|
||||||
|
0 /* carrierId */,
|
||||||
|
0 /* profileClass */);
|
||||||
|
|
||||||
private final Context mMockContext = mock(Context.class);
|
private final Context mMockContext = mock(Context.class);
|
||||||
private final VcnManagementService.Dependencies mMockDeps =
|
private final VcnManagementService.Dependencies mMockDeps =
|
||||||
mock(VcnManagementService.Dependencies.class);
|
mock(VcnManagementService.Dependencies.class);
|
||||||
private final TestLooper mTestLooper = new TestLooper();
|
private final TestLooper mTestLooper = new TestLooper();
|
||||||
private final ConnectivityManager mConnMgr = mock(ConnectivityManager.class);
|
private final ConnectivityManager mConnMgr = mock(ConnectivityManager.class);
|
||||||
|
private final TelephonyManager mTelMgr = mock(TelephonyManager.class);
|
||||||
|
private final SubscriptionManager mSubMgr = mock(SubscriptionManager.class);
|
||||||
private final VcnManagementService mVcnMgmtSvc;
|
private final VcnManagementService mVcnMgmtSvc;
|
||||||
|
|
||||||
public VcnManagementServiceTest() {
|
public VcnManagementServiceTest() throws Exception {
|
||||||
doReturn(Context.CONNECTIVITY_SERVICE)
|
setupSystemService(mConnMgr, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class);
|
||||||
.when(mMockContext)
|
setupSystemService(mTelMgr, Context.TELEPHONY_SERVICE, TelephonyManager.class);
|
||||||
.getSystemServiceName(ConnectivityManager.class);
|
setupSystemService(
|
||||||
doReturn(mConnMgr).when(mMockContext).getSystemService(Context.CONNECTIVITY_SERVICE);
|
mSubMgr, Context.TELEPHONY_SUBSCRIPTION_SERVICE, SubscriptionManager.class);
|
||||||
|
|
||||||
doReturn(mTestLooper.getLooper()).when(mMockDeps).getLooper();
|
doReturn(mTestLooper.getLooper()).when(mMockDeps).getLooper();
|
||||||
|
doReturn(Process.FIRST_APPLICATION_UID).when(mMockDeps).getBinderCallingUid();
|
||||||
|
|
||||||
|
setupMockedCarrierPrivilege(true);
|
||||||
mVcnMgmtSvc = new VcnManagementService(mMockContext, mMockDeps);
|
mVcnMgmtSvc = new VcnManagementService(mMockContext, mMockDeps);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setupSystemService(Object service, String name, Class<?> serviceClass) {
|
||||||
|
doReturn(name).when(mMockContext).getSystemServiceName(serviceClass);
|
||||||
|
doReturn(service).when(mMockContext).getSystemService(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupMockedCarrierPrivilege(boolean isPrivileged) {
|
||||||
|
doReturn(Collections.singletonList(TEST_SUBSCRIPTION_INFO))
|
||||||
|
.when(mSubMgr)
|
||||||
|
.getSubscriptionsInGroup(any());
|
||||||
|
doReturn(isPrivileged)
|
||||||
|
.when(mTelMgr)
|
||||||
|
.hasCarrierPrivileges(eq(TEST_SUBSCRIPTION_INFO.getSubscriptionId()));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSystemReady() throws Exception {
|
public void testSystemReady() throws Exception {
|
||||||
mVcnMgmtSvc.systemReady();
|
mVcnMgmtSvc.systemReady();
|
||||||
@@ -59,4 +114,74 @@ public class VcnManagementServiceTest {
|
|||||||
verify(mConnMgr)
|
verify(mConnMgr)
|
||||||
.registerNetworkProvider(any(VcnManagementService.VcnNetworkProvider.class));
|
.registerNetworkProvider(any(VcnManagementService.VcnNetworkProvider.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetVcnConfigRequiresNonSystemServer() throws Exception {
|
||||||
|
doReturn(Process.SYSTEM_UID).when(mMockDeps).getBinderCallingUid();
|
||||||
|
|
||||||
|
try {
|
||||||
|
mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, new VcnConfig.Builder().build());
|
||||||
|
fail("Expected IllegalStateException exception for system server");
|
||||||
|
} catch (IllegalStateException expected) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetVcnConfigRequiresSystemUser() throws Exception {
|
||||||
|
doReturn(UserHandle.getUid(UserHandle.MIN_SECONDARY_USER_ID, Process.FIRST_APPLICATION_UID))
|
||||||
|
.when(mMockDeps)
|
||||||
|
.getBinderCallingUid();
|
||||||
|
|
||||||
|
try {
|
||||||
|
mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, new VcnConfig.Builder().build());
|
||||||
|
fail("Expected security exception for non system user");
|
||||||
|
} catch (SecurityException expected) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetVcnConfigRequiresCarrierPrivileges() throws Exception {
|
||||||
|
setupMockedCarrierPrivilege(false);
|
||||||
|
|
||||||
|
try {
|
||||||
|
mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, new VcnConfig.Builder().build());
|
||||||
|
fail("Expected security exception for missing carrier privileges");
|
||||||
|
} catch (SecurityException expected) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testClearVcnConfigRequiresNonSystemServer() throws Exception {
|
||||||
|
doReturn(Process.SYSTEM_UID).when(mMockDeps).getBinderCallingUid();
|
||||||
|
|
||||||
|
try {
|
||||||
|
mVcnMgmtSvc.clearVcnConfig(TEST_UUID_1);
|
||||||
|
fail("Expected IllegalStateException exception for system server");
|
||||||
|
} catch (IllegalStateException expected) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testClearVcnConfigRequiresSystemUser() throws Exception {
|
||||||
|
doReturn(UserHandle.getUid(UserHandle.MIN_SECONDARY_USER_ID, Process.FIRST_APPLICATION_UID))
|
||||||
|
.when(mMockDeps)
|
||||||
|
.getBinderCallingUid();
|
||||||
|
|
||||||
|
try {
|
||||||
|
mVcnMgmtSvc.clearVcnConfig(TEST_UUID_1);
|
||||||
|
fail("Expected security exception for non system user");
|
||||||
|
} catch (SecurityException expected) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testClearVcnConfigRequiresCarrierPrivileges() throws Exception {
|
||||||
|
setupMockedCarrierPrivilege(false);
|
||||||
|
|
||||||
|
try {
|
||||||
|
mVcnMgmtSvc.clearVcnConfig(TEST_UUID_1);
|
||||||
|
fail("Expected security exception for missing carrier privileges");
|
||||||
|
} catch (SecurityException expected) {
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user