Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ecff86d3 authored by Ying Xu's avatar Ying Xu Committed by Android (Google) Code Review
Browse files

Merge "Delete the VPN profile if the owner app is removed" into main

parents 597b4243 0b5a5f8c
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -77,3 +77,14 @@ flag {
  description: "Flag for adding CREATE_APP_SPECIFIC_NETWORK permission"
  bug: "435313135"
}

flag {
  name: "delete_vpn_profile_when_app_uninstalled"
  namespace: "android_core_networking"
  is_exported: false
  description: "Flag for deleting VPN profile when the provisioning app is uninstalled"
  bug: "440083803"
  metadata {
    purpose: PURPOSE_BUGFIX
  }
}
+6 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.server;

import static android.Manifest.permission.NETWORK_STACK;
import static android.net.platform.flags.Flags.deleteVpnProfileWhenAppUninstalled;

import static com.android.net.module.util.PermissionUtils.enforceAnyPermissionOf;
import static com.android.net.module.util.PermissionUtils.enforceNetworkStackPermission;
@@ -889,6 +890,11 @@ public class VpnManagerService extends IVpnManager.Stub {
                log("Removing always-on VPN package " + packageName + " for user "
                        + userId);
                vpn.setAlwaysOnPackage(null, false, null);
            } else if (deleteVpnProfileWhenAppUninstalled()
                    && TextUtils.equals(vpn.getPackage(), packageName)) {
                log("Removing VPN package " + packageName + " for user "
                        + userId);
                vpn.deleteVpnProfileDueToAppRemoval(packageName, uid);
            }

            vpn.refreshPlatformVpnAppExclusionList();
+50 −2
Original line number Diff line number Diff line
@@ -602,6 +602,16 @@ public class Vpn {
            }
        }

        /** Verify the binder calling UID is the one passed in arguments or the SYSTEM_UID */
        public void verifyCallingUidOrSystemUidAndPackage(
                Context context, String packageName, int userId) {
            final int callingUid = Binder.getCallingUid();
            if (getAppUid(context, packageName, userId) != callingUid
                    && callingUid != Process.SYSTEM_UID) {
                throw new SecurityException(packageName + " does not belong to uid " + callingUid);
            }
        }

        /**
         * @see VpnConnectivityMetrics.
         *
@@ -1308,8 +1318,12 @@ public class Vpn {
        // We can't just check that packageName matches mPackage, because if the app was uninstalled
        // and reinstalled it will no longer be prepared. Similarly if there is a shared UID, the
        // calling package may not be the same as the prepared package. Check both UID and package.
        return getAppUid(mContext, packageName, mUserId) == mOwnerUID
                && mPackage.equals(packageName);
        return isCurrentPreparedPackage(packageName, getAppUid(mContext, packageName, mUserId));
    }

    @GuardedBy("this")
    private boolean isCurrentPreparedPackage(String packageName, int uid) {
        return uid == mOwnerUID && mPackage.equals(packageName);
    }

    /** Prepare the VPN for the given package. Does not perform permission checks. */
@@ -4091,6 +4105,10 @@ public class Vpn {
        mDeps.verifyCallingUidAndPackage(mContext, packageName, mUserId);
    }

    private void verifyCallingUidOrSystemUidAndPackage(String packageName) {
        mDeps.verifyCallingUidOrSystemUidAndPackage(mContext, packageName, mUserId);
    }

    @VisibleForTesting
    String getProfileNameForPackage(String packageName) {
        return Credentials.PLATFORM_VPN + mUserId + "_" + packageName;
@@ -4159,6 +4177,10 @@ public class Vpn {
        return isCurrentPreparedPackage(packageName) && isIkev2VpnRunner();
    }

    private boolean isCurrentIkev2VpnLocked(@NonNull String packageName, int uid) {
        return isCurrentPreparedPackage(packageName, uid) && isIkev2VpnRunner();
    }

    /**
     * Deletes an app-provisioned VPN profile.
     *
@@ -4190,6 +4212,32 @@ public class Vpn {
        }
    }

    /**
     * Deletes an app-provisioned VPN profile because the provisioning app has been uninstalled.
     *
     * @param packageName the package name of the app provisioning this profile
     * @param uid the uid of the app provisioning this profile
     */
    public synchronized void deleteVpnProfileDueToAppRemoval(
            @NonNull String packageName, int uid) {
        requireNonNull(packageName, "No package name provided");

        verifyCallingUidOrSystemUidAndPackage(packageName);
        enforceNotRestrictedUser();

        final long token = Binder.clearCallingIdentity();
        try {
            // If this profile is providing the current VPN, turn it off.
            if (isCurrentIkev2VpnLocked(packageName, uid)) {
                prepareInternal(VpnConfig.LEGACY_VPN);
            }

            getVpnProfileStore().remove(getProfileNameForPackage(packageName));
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }

    /**
     * Retrieves the VpnProfile.
     *
+19 −0
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.server;

import static android.net.platform.flags.Flags.FLAG_DELETE_VPN_PROFILE_WHEN_APP_UNINSTALLED;

import static com.android.testutils.ContextUtils.mockService;
import static com.android.testutils.MiscAsserts.assertThrows;

@@ -45,6 +47,8 @@ import android.os.INetworkManagementService;
import android.os.Looper;
import android.os.UserHandle;
import android.os.UserManager;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.security.Credentials;

import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -57,6 +61,7 @@ import com.android.server.net.LockdownVpnTracker;
import com.android.testutils.HandlerUtils;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -69,6 +74,9 @@ import java.util.List;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class VpnManagerServiceTest extends VpnTestBase {

    @Rule
    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
    private static final String CONTEXT_ATTRIBUTION_TAG = "VPN_MANAGER";

    private static final int TIMEOUT_MS = 2_000;
@@ -428,4 +436,15 @@ public class VpnManagerServiceTest extends VpnTestBase {
        mService.listFromVpnProfileStore(name);
        verify(mVpnProfileStore).list(name);
    }

    @Test
    @EnableFlags(FLAG_DELETE_VPN_PROFILE_WHEN_APP_UNINSTALLED)
    public void testRemoveVpnProfileOnPackageRemoved() {
        mService.startVpnProfile(TEST_VPN_PKG);
        doReturn(null).when(mVpn).getAlwaysOnPackage();
        doReturn(PKGS[0]).when(mVpn).getPackage();

        onPackageRemoved(PKGS[0], PKG_UIDS[0], false /* isReplacing */);
        verify(mVpn).deleteVpnProfileDueToAppRemoval(PKGS[0], PKG_UIDS[0]);
    }
}
+20 −0
Original line number Diff line number Diff line
@@ -1324,6 +1324,26 @@ public class VpnTest extends VpnTestBase {
                .remove(eq(vpn.getProfileNameForPackage(TEST_VPN_PKG)));
    }

    @Test
    public void testDeleteVpnProfileDueToAppRemoval() throws Exception {
        final Vpn vpn = createVpn(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN);

        when(mVpnProfileStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG)))
                .thenReturn(mVpnProfile.encode());

        vpn.startVpnProfile(TEST_VPN_PKG);
        verifyPlatformVpnIsActivated(TEST_VPN_PKG);

        // This mock is to make sure verifyCallingUidOrSystemUidAndPackage() would pass, the test
        // will fail since the unit test UID is not the SYSTEM_UID.
        when(mPackageManager.getPackageUidAsUser(any(), anyInt())).thenReturn(Process.myUid());
        vpn.deleteVpnProfileDueToAppRemoval(
                TEST_VPN_PKG, UserHandle.getUid(PRIMARY_USER.id, Process.myUid()));

        verify(mVpnProfileStore)
                .remove(eq(vpn.getProfileNameForPackage(TEST_VPN_PKG)));
        verifyPlatformVpnIsDeactivated(TEST_VPN_PKG);
    }
    @Test
    public void testDeleteVpnProfileRestrictedUser() throws Exception {
        final Vpn vpn =