summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/VcnManagementService.java61
-rw-r--r--tests/vcn/java/com/android/server/VcnManagementServiceTest.java49
2 files changed, 104 insertions, 6 deletions
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index 2fdc7965675d..76db0192cb00 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -31,16 +31,19 @@ import android.net.vcn.VcnConfig;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.IBinder;
import android.os.Looper;
import android.os.ParcelUuid;
import android.os.PersistableBundle;
import android.os.Process;
+import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.os.UserHandle;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.util.ArrayMap;
+import android.util.Log;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
@@ -155,6 +158,11 @@ public class VcnManagementService extends IVcnManagementService.Stub {
@NonNull private final PersistableBundleUtils.LockingReadWriteHelper mConfigDiskRwHelper;
+ @GuardedBy("mLock")
+ @NonNull
+ private final Map<IBinder, PolicyListenerBinderDeath> mRegisteredPolicyListeners =
+ new ArrayMap<>();
+
@VisibleForTesting(visibility = Visibility.PRIVATE)
VcnManagementService(@NonNull Context context, @NonNull Dependencies deps) {
mContext = requireNonNull(context, "Missing context");
@@ -497,19 +505,60 @@ public class VcnManagementService extends IVcnManagementService.Stub {
}
}
+ /** Binder death recipient used to remove a registered policy listener. */
+ private class PolicyListenerBinderDeath implements Binder.DeathRecipient {
+ @NonNull private final IVcnUnderlyingNetworkPolicyListener mListener;
+
+ PolicyListenerBinderDeath(@NonNull IVcnUnderlyingNetworkPolicyListener listener) {
+ mListener = listener;
+ }
+
+ @Override
+ public void binderDied() {
+ Log.e(TAG, "app died without removing VcnUnderlyingNetworkPolicyListener");
+ removeVcnUnderlyingNetworkPolicyListener(mListener);
+ }
+ }
+
/** Adds the provided listener for receiving VcnUnderlyingNetworkPolicy updates. */
+ @GuardedBy("mLock")
@Override
public void addVcnUnderlyingNetworkPolicyListener(
- IVcnUnderlyingNetworkPolicyListener listener) {
- // TODO(b/175739863): implement policy listener registration
- throw new UnsupportedOperationException("Not yet implemented");
+ @NonNull IVcnUnderlyingNetworkPolicyListener listener) {
+ requireNonNull(listener, "listener was null");
+
+ mContext.enforceCallingPermission(
+ android.Manifest.permission.NETWORK_FACTORY,
+ "Must have permission NETWORK_FACTORY to register a policy listener");
+
+ PolicyListenerBinderDeath listenerBinderDeath = new PolicyListenerBinderDeath(listener);
+
+ 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();
+ }
+ }
}
/** Removes the provided listener from receiving VcnUnderlyingNetworkPolicy updates. */
+ @GuardedBy("mLock")
@Override
public void removeVcnUnderlyingNetworkPolicyListener(
- IVcnUnderlyingNetworkPolicyListener listener) {
- // TODO(b/175739863): implement policy listener unregistration
- throw new UnsupportedOperationException("Not yet implemented");
+ @NonNull IVcnUnderlyingNetworkPolicyListener listener) {
+ requireNonNull(listener, "listener was null");
+
+ synchronized (mLock) {
+ PolicyListenerBinderDeath listenerBinderDeath =
+ mRegisteredPolicyListeners.remove(listener.asBinder());
+
+ if (listenerBinderDeath != null) {
+ listener.asBinder().unlinkToDeath(listenerBinderDeath, 0 /* flags */);
+ }
+ }
}
}
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index 696110f01869..29cfdb6fd5c6 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -23,10 +23,15 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.argThat;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -35,8 +40,10 @@ import static org.mockito.Mockito.verify;
import android.app.AppOpsManager;
import android.content.Context;
import android.net.ConnectivityManager;
+import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
import android.net.vcn.VcnConfig;
import android.net.vcn.VcnConfigTest;
+import android.os.IBinder;
import android.os.ParcelUuid;
import android.os.PersistableBundle;
import android.os.Process;
@@ -126,6 +133,10 @@ public class VcnManagementServiceTest {
private final VcnManagementService mVcnMgmtSvc;
+ private final IVcnUnderlyingNetworkPolicyListener mMockPolicyListener =
+ mock(IVcnUnderlyingNetworkPolicyListener.class);
+ private final IBinder mMockIBinder = mock(IBinder.class);
+
public VcnManagementServiceTest() throws Exception {
setupSystemService(mConnMgr, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class);
setupSystemService(mTelMgr, Context.TELEPHONY_SERVICE, TelephonyManager.class);
@@ -169,6 +180,8 @@ public class VcnManagementServiceTest {
setupMockedCarrierPrivilege(true);
mVcnMgmtSvc = new VcnManagementService(mMockContext, mMockDeps);
+ doReturn(mMockIBinder).when(mMockPolicyListener).asBinder();
+
// Make sure the profiles are loaded.
mTestLooper.dispatchAll();
}
@@ -438,4 +451,40 @@ public class VcnManagementServiceTest {
mVcnMgmtSvc.clearVcnConfig(TEST_UUID_2);
verify(vcnInstance).teardownAsynchronously();
}
+
+ @Test
+ public void testAddVcnUnderlyingNetworkPolicyListener() throws Exception {
+ doNothing()
+ .when(mMockContext)
+ .enforceCallingPermission(eq(android.Manifest.permission.NETWORK_FACTORY), any());
+
+ mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
+
+ verify(mMockIBinder).linkToDeath(any(), anyInt());
+ }
+
+ @Test(expected = SecurityException.class)
+ public void testAddVcnUnderlyingNetworkPolicyListenerInvalidPermission() {
+ doThrow(new SecurityException())
+ .when(mMockContext)
+ .enforceCallingPermission(eq(android.Manifest.permission.NETWORK_FACTORY), any());
+
+ mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
+ }
+
+ @Test
+ public void testRemoveVcnUnderlyingNetworkPolicyListener() {
+ // verify listener added
+ doNothing()
+ .when(mMockContext)
+ .enforceCallingPermission(eq(android.Manifest.permission.NETWORK_FACTORY), any());
+ mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
+
+ mVcnMgmtSvc.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
+ }
+
+ @Test
+ public void testRemoveVcnUnderlyingNetworkPolicyListenerNeverRegistered() {
+ mVcnMgmtSvc.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
+ }
}