Merge changes I14740854,Id1c2dc28,I4168c868 am: 3b0cb56475 am: 06085e6ab3 am: f5d4826ef1

Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1607195

MUST ONLY BE SUBMITTED BY AUTOMERGER

Change-Id: Ied6022110fd73b4880a4a34caba03ae821e111a7
This commit is contained in:
Cody Kesting
2021-03-09 03:03:56 +00:00
committed by Automerger Merge Worker
4 changed files with 210 additions and 80 deletions

View File

@@ -16,6 +16,9 @@
package com.android.server;
import static android.net.vcn.VcnManager.VCN_STATUS_CODE_ACTIVE;
import static android.net.vcn.VcnManager.VCN_STATUS_CODE_INACTIVE;
import static android.net.vcn.VcnManager.VCN_STATUS_CODE_NOT_CONFIGURED;
import static android.net.vcn.VcnManager.VCN_STATUS_CODE_SAFE_MODE;
import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
@@ -37,6 +40,7 @@ import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
import android.net.vcn.VcnConfig;
import android.net.vcn.VcnManager;
import android.net.vcn.VcnManager.VcnErrorCode;
import android.net.vcn.VcnManager.VcnStatusCode;
import android.net.vcn.VcnUnderlyingNetworkPolicy;
import android.net.wifi.WifiInfo;
import android.os.Binder;
@@ -420,6 +424,11 @@ public class VcnManagementService extends IVcnManagementService.Stub {
// Carrier App manually removing/adding a VcnConfig.
if (mVcns.get(uuidToTeardown) == instanceToTeardown) {
stopVcnLocked(uuidToTeardown);
// TODO(b/181789060): invoke asynchronously after Vcn notifies
// through VcnCallback
notifyAllPermissionedStatusCallbacksLocked(
uuidToTeardown, VCN_STATUS_CODE_INACTIVE);
}
}
}, instanceToTeardown, CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
@@ -453,6 +462,17 @@ public class VcnManagementService extends IVcnManagementService.Stub {
}
}
@GuardedBy("mLock")
private void notifyAllPermissionedStatusCallbacksLocked(
@NonNull ParcelUuid subGroup, @VcnStatusCode int statusCode) {
for (final VcnStatusCallbackInfo cbInfo : mRegisteredStatusCallbacks.values()) {
if (isCallbackPermissioned(cbInfo, subGroup)) {
Binder.withCleanCallingIdentity(
() -> cbInfo.mCallback.onVcnStatusChanged(statusCode));
}
}
}
@GuardedBy("mLock")
private void startVcnLocked(@NonNull ParcelUuid subscriptionGroup, @NonNull VcnConfig config) {
Slog.v(TAG, "Starting VCN config for subGrp: " + subscriptionGroup);
@@ -469,6 +489,9 @@ public class VcnManagementService extends IVcnManagementService.Stub {
// Now that a new VCN has started, notify all registered listeners to refresh their
// UnderlyingNetworkPolicy.
notifyAllPolicyListenersLocked();
// TODO(b/181789060): invoke asynchronously after Vcn notifies through VcnCallback
notifyAllPermissionedStatusCallbacksLocked(subscriptionGroup, VCN_STATUS_CODE_ACTIVE);
}
@GuardedBy("mLock")
@@ -477,7 +500,16 @@ public class VcnManagementService extends IVcnManagementService.Stub {
Slog.v(TAG, "Starting or updating VCN config for subGrp: " + subscriptionGroup);
if (mVcns.containsKey(subscriptionGroup)) {
mVcns.get(subscriptionGroup).updateConfig(config);
final Vcn vcn = mVcns.get(subscriptionGroup);
final boolean isActive = vcn.isActive();
vcn.updateConfig(config);
// Only notify VcnStatusCallbacks if this VCN was previously in Safe Mode
if (!isActive) {
// TODO(b/181789060): invoke asynchronously after Vcn notifies through VcnCallback
notifyAllPermissionedStatusCallbacksLocked(
subscriptionGroup, VCN_STATUS_CODE_ACTIVE);
}
} else {
startVcnLocked(subscriptionGroup, config);
}
@@ -530,9 +562,17 @@ public class VcnManagementService extends IVcnManagementService.Stub {
Binder.withCleanCallingIdentity(() -> {
synchronized (mLock) {
mConfigs.remove(subscriptionGroup);
final boolean vcnExists = mVcns.containsKey(subscriptionGroup);
stopVcnLocked(subscriptionGroup);
if (vcnExists) {
// TODO(b/181789060): invoke asynchronously after Vcn notifies through
// VcnCallback
notifyAllPermissionedStatusCallbacksLocked(
subscriptionGroup, VCN_STATUS_CODE_NOT_CONFIGURED);
}
writeConfigsToDiskLocked();
}
});
@@ -603,18 +643,20 @@ public class VcnManagementService extends IVcnManagementService.Stub {
android.Manifest.permission.NETWORK_FACTORY,
"Must have permission NETWORK_FACTORY to register a policy listener");
PolicyListenerBinderDeath listenerBinderDeath = new PolicyListenerBinderDeath(listener);
Binder.withCleanCallingIdentity(() -> {
PolicyListenerBinderDeath listenerBinderDeath = new PolicyListenerBinderDeath(listener);
synchronized (mLock) {
mRegisteredPolicyListeners.put(listener.asBinder(), listenerBinderDeath);
synchronized (mLock) {
mRegisteredPolicyListeners.put(listener.asBinder(), listenerBinderDeath);
try {
listener.asBinder().linkToDeath(listenerBinderDeath, 0 /* flags */);
} catch (RemoteException e) {
// Remote binder already died - cleanup registered Listener
listenerBinderDeath.binderDied();
try {
listener.asBinder().linkToDeath(listenerBinderDeath, 0 /* flags */);
} catch (RemoteException e) {
// Remote binder already died - cleanup registered Listener
listenerBinderDeath.binderDied();
}
}
}
});
}
/** Removes the provided listener from receiving VcnUnderlyingNetworkPolicy updates. */
@@ -624,14 +666,31 @@ public class VcnManagementService extends IVcnManagementService.Stub {
@NonNull IVcnUnderlyingNetworkPolicyListener listener) {
requireNonNull(listener, "listener was null");
synchronized (mLock) {
PolicyListenerBinderDeath listenerBinderDeath =
mRegisteredPolicyListeners.remove(listener.asBinder());
Binder.withCleanCallingIdentity(() -> {
synchronized (mLock) {
PolicyListenerBinderDeath listenerBinderDeath =
mRegisteredPolicyListeners.remove(listener.asBinder());
if (listenerBinderDeath != null) {
listener.asBinder().unlinkToDeath(listenerBinderDeath, 0 /* flags */);
if (listenerBinderDeath != null) {
listener.asBinder().unlinkToDeath(listenerBinderDeath, 0 /* flags */);
}
}
});
}
private int getSubIdForNetworkCapabilities(@NonNull NetworkCapabilities networkCapabilities) {
if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)
&& networkCapabilities.getNetworkSpecifier() instanceof TelephonyNetworkSpecifier) {
TelephonyNetworkSpecifier telephonyNetworkSpecifier =
(TelephonyNetworkSpecifier) networkCapabilities.getNetworkSpecifier();
return telephonyNetworkSpecifier.getSubscriptionId();
} else if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)
&& networkCapabilities.getTransportInfo() instanceof WifiInfo) {
WifiInfo wifiInfo = (WifiInfo) networkCapabilities.getTransportInfo();
return mDeps.getSubIdForWifiInfo(wifiInfo);
}
return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
}
/**
@@ -651,51 +710,47 @@ public class VcnManagementService extends IVcnManagementService.Stub {
"Must have permission NETWORK_FACTORY or be the SystemServer to get underlying"
+ " Network policies");
// Defensive copy in case this call is in-process and the given NetworkCapabilities mutates
networkCapabilities = new NetworkCapabilities(networkCapabilities);
return Binder.withCleanCallingIdentity(() -> {
// Defensive copy in case this call is in-process and the given NetworkCapabilities
// mutates
final NetworkCapabilities ncCopy = new NetworkCapabilities(networkCapabilities);
int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)
&& networkCapabilities.getNetworkSpecifier() instanceof TelephonyNetworkSpecifier) {
TelephonyNetworkSpecifier telephonyNetworkSpecifier =
(TelephonyNetworkSpecifier) networkCapabilities.getNetworkSpecifier();
subId = telephonyNetworkSpecifier.getSubscriptionId();
} else if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)
&& networkCapabilities.getTransportInfo() instanceof WifiInfo) {
WifiInfo wifiInfo = (WifiInfo) networkCapabilities.getTransportInfo();
subId = mDeps.getSubIdForWifiInfo(wifiInfo);
}
final int subId = getSubIdForNetworkCapabilities(ncCopy);
boolean isVcnManagedNetwork = false;
boolean isRestrictedCarrierWifi = false;
if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
synchronized (mLock) {
ParcelUuid subGroup = mLastSnapshot.getGroupForSubId(subId);
boolean isVcnManagedNetwork = false;
boolean isRestrictedCarrierWifi = false;
if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
synchronized (mLock) {
ParcelUuid subGroup = mLastSnapshot.getGroupForSubId(subId);
final Vcn vcn = mVcns.get(subGroup);
if (vcn != null) {
if (vcn.isActive()) {
isVcnManagedNetwork = true;
}
Vcn vcn = mVcns.get(subGroup);
if (vcn != null) {
if (vcn.isActive()) {
isVcnManagedNetwork = true;
}
if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
// Carrier WiFi always restricted if VCN exists (even in safe mode).
isRestrictedCarrierWifi = true;
if (ncCopy.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
// Carrier WiFi always restricted if VCN exists (even in safe mode).
isRestrictedCarrierWifi = true;
}
}
}
}
}
if (isVcnManagedNetwork) {
networkCapabilities.removeCapability(
NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
}
final NetworkCapabilities.Builder ncBuilder = new NetworkCapabilities.Builder(ncCopy);
if (isRestrictedCarrierWifi) {
networkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
}
if (isVcnManagedNetwork) {
ncBuilder.removeCapability(
NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
}
return new VcnUnderlyingNetworkPolicy(false /* isTearDownRequested */, networkCapabilities);
if (isRestrictedCarrierWifi) {
ncBuilder.removeCapability(
NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
}
return new VcnUnderlyingNetworkPolicy(
false /* isTearDownRequested */, ncBuilder.build());
});
}
/** Binder death recipient used to remove registered VcnStatusCallbacks. */
@@ -856,16 +911,7 @@ public class VcnManagementService extends IVcnManagementService.Stub {
}
notifyAllPolicyListenersLocked();
// Notify all registered StatusCallbacks for this subGroup
for (VcnStatusCallbackInfo cbInfo : mRegisteredStatusCallbacks.values()) {
if (isCallbackPermissioned(cbInfo, mSubGroup)) {
Binder.withCleanCallingIdentity(
() ->
cbInfo.mCallback.onVcnStatusChanged(
VCN_STATUS_CODE_SAFE_MODE));
}
}
notifyAllPermissionedStatusCallbacksLocked(mSubGroup, VCN_STATUS_CODE_SAFE_MODE);
}
}

View File

@@ -128,7 +128,6 @@ public class Vcn extends Handler {
* from VcnManagementService, and therefore cannot rely on guarantees of running on the VCN
* Looper.
*/
// TODO(b/179429339): update when exiting safemode (when a new VcnConfig is provided)
private final AtomicBoolean mIsActive = new AtomicBoolean(true);
public Vcn(
@@ -203,7 +202,8 @@ public class Vcn extends Handler {
@Override
public void handleMessage(@NonNull Message msg) {
if (!isActive()) {
// Ignore if this Vcn is not active and we're not receiving new configs
if (!isActive() && msg.what != MSG_EVENT_CONFIG_UPDATED) {
return;
}
@@ -237,7 +237,13 @@ public class Vcn extends Handler {
mConfig = config;
// TODO: Reevaluate active VcnGatewayConnection(s)
// TODO(b/181815405): Reevaluate active VcnGatewayConnection(s)
if (!mIsActive.getAndSet(true)) {
// If this VCN was not previously active, it is exiting Safe Mode. Re-register the
// request listener to get NetworkRequests again (and all cached requests).
mVcnContext.getVcnNetworkProvider().registerListener(mRequestListener);
}
}
private void handleTeardown() {
@@ -253,6 +259,8 @@ public class Vcn extends Handler {
private void handleEnterSafeMode() {
handleTeardown();
mVcnGatewayConnections.clear();
mVcnCallback.onEnteredSafeMode();
}

View File

@@ -460,6 +460,34 @@ public class VcnManagementServiceTest {
verify(mConfigReadWriteHelper).writeToDisk(any(PersistableBundle.class));
}
@Test
public void testSetVcnConfigNotifiesStatusCallback() throws Exception {
mVcnMgmtSvc.systemReady();
doReturn(true)
.when(mLocationPermissionChecker)
.checkLocationPermission(eq(TEST_PACKAGE_NAME), any(), eq(TEST_UID), any());
triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_2));
mVcnMgmtSvc.registerVcnStatusCallback(TEST_UUID_2, mMockStatusCallback, TEST_PACKAGE_NAME);
verify(mMockStatusCallback).onVcnStatusChanged(VcnManager.VCN_STATUS_CODE_NOT_CONFIGURED);
// Use a different UUID to simulate a new VCN config.
mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
verify(mMockStatusCallback).onVcnStatusChanged(VcnManager.VCN_STATUS_CODE_ACTIVE);
}
@Test
public void testSetVcnConfigInSafeModeNotifiesStatusCallback() throws Exception {
setupSubscriptionAndStartVcn(TEST_SUBSCRIPTION_ID, TEST_UUID_2, false /* isActive */);
mVcnMgmtSvc.registerVcnStatusCallback(TEST_UUID_2, mMockStatusCallback, TEST_PACKAGE_NAME);
verify(mMockStatusCallback).onVcnStatusChanged(VcnManager.VCN_STATUS_CODE_SAFE_MODE);
mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
verify(mMockStatusCallback).onVcnStatusChanged(VcnManager.VCN_STATUS_CODE_ACTIVE);
}
@Test
public void testClearVcnConfigRequiresNonSystemServer() throws Exception {
doReturn(Process.SYSTEM_UID).when(mMockDeps).getBinderCallingUid();
@@ -502,6 +530,17 @@ public class VcnManagementServiceTest {
verify(mConfigReadWriteHelper).writeToDisk(any(PersistableBundle.class));
}
@Test
public void testClearVcnConfigNotifiesStatusCallback() throws Exception {
setupSubscriptionAndStartVcn(TEST_SUBSCRIPTION_ID, TEST_UUID_2, true /* isActive */);
mVcnMgmtSvc.registerVcnStatusCallback(TEST_UUID_2, mMockStatusCallback, TEST_PACKAGE_NAME);
verify(mMockStatusCallback).onVcnStatusChanged(VcnManager.VCN_STATUS_CODE_ACTIVE);
mVcnMgmtSvc.clearVcnConfig(TEST_UUID_2);
verify(mMockStatusCallback).onVcnStatusChanged(VcnManager.VCN_STATUS_CODE_NOT_CONFIGURED);
}
@Test
public void testSetVcnConfigClearVcnConfigStartsUpdatesAndTeardsDownVcns() throws Exception {
// Use a different UUID to simulate a new VCN config.

View File

@@ -22,7 +22,9 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_MMS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.argThat;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
@@ -48,6 +50,7 @@ import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import java.util.Arrays;
import java.util.Set;
import java.util.UUID;
@@ -58,7 +61,7 @@ public class VcnTest {
private static final int PROVIDER_ID = 5;
private static final int[][] TEST_CAPS =
new int[][] {
new int[] {NET_CAPABILITY_INTERNET, NET_CAPABILITY_MMS},
new int[] {NET_CAPABILITY_MMS, NET_CAPABILITY_INTERNET},
new int[] {NET_CAPABILITY_DUN}
};
@@ -155,14 +158,6 @@ public class VcnTest {
}
}
@Test
public void testGatewayEnteringSafeModeNotifiesVcn() {
final NetworkRequestListener requestListener = verifyAndGetRequestListener();
for (final int capability : VcnGatewayConnectionConfigTest.EXPOSED_CAPS) {
startVcnGatewayWithCapabilities(requestListener, capability);
}
}
private void triggerVcnRequestListeners(NetworkRequestListener requestListener) {
for (final int[] caps : TEST_CAPS) {
startVcnGatewayWithCapabilities(requestListener, caps);
@@ -188,8 +183,20 @@ public class VcnTest {
return gatewayConnections;
}
private void verifySafeMode(
NetworkRequestListener requestListener,
Set<VcnGatewayConnection> expectedGatewaysTornDown) {
assertFalse(mVcn.isActive());
assertTrue(mVcn.getVcnGatewayConnections().isEmpty());
for (final VcnGatewayConnection gatewayConnection : expectedGatewaysTornDown) {
verify(gatewayConnection).teardownAsynchronously();
}
verify(mVcnNetworkProvider).unregisterListener(requestListener);
verify(mVcnCallback).onEnteredSafeMode();
}
@Test
public void testGatewayEnteringSafemodeNotifiesVcn() {
public void testGatewayEnteringSafeModeNotifiesVcn() {
final NetworkRequestListener requestListener = verifyAndGetRequestListener();
final Set<VcnGatewayConnection> gatewayConnections =
startGatewaysAndGetGatewayConnections(requestListener);
@@ -200,12 +207,7 @@ public class VcnTest {
statusCallback.onEnteredSafeMode();
mTestLooper.dispatchAll();
assertFalse(mVcn.isActive());
for (final VcnGatewayConnection gatewayConnection : gatewayConnections) {
verify(gatewayConnection).teardownAsynchronously();
}
verify(mVcnNetworkProvider).unregisterListener(requestListener);
verify(mVcnCallback).onEnteredSafeMode();
verifySafeMode(requestListener, gatewayConnections);
}
@Test
@@ -234,4 +236,39 @@ public class VcnTest {
any(),
mGatewayStatusCallbackCaptor.capture());
}
@Test
public void testUpdateConfigExitsSafeMode() {
final NetworkRequestListener requestListener = verifyAndGetRequestListener();
final Set<VcnGatewayConnection> gatewayConnections =
new ArraySet<>(startGatewaysAndGetGatewayConnections(requestListener));
final VcnGatewayStatusCallback statusCallback = mGatewayStatusCallbackCaptor.getValue();
statusCallback.onEnteredSafeMode();
mTestLooper.dispatchAll();
verifySafeMode(requestListener, gatewayConnections);
doAnswer(invocation -> {
final NetworkRequestListener listener = invocation.getArgument(0);
triggerVcnRequestListeners(listener);
return null;
}).when(mVcnNetworkProvider).registerListener(eq(requestListener));
mVcn.updateConfig(mConfig);
mTestLooper.dispatchAll();
// Registered on start, then re-registered with new configs
verify(mVcnNetworkProvider, times(2)).registerListener(eq(requestListener));
assertTrue(mVcn.isActive());
for (final int[] caps : TEST_CAPS) {
// Expect each gateway connection created on initial startup, and again with new configs
verify(mDeps, times(2))
.newVcnGatewayConnection(
eq(mVcnContext),
eq(TEST_SUB_GROUP),
eq(mSubscriptionSnapshot),
argThat(config -> Arrays.equals(caps, config.getExposedCapabilities())),
any());
}
}
}