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

Commit 52f9de42 authored by lucaslin's avatar lucaslin Committed by Lucas Lin
Browse files

Send VPN manager event when VPN always-on status is changed

Bug: 225010642
Test: atest FrameworksNetTests:VpnTest
Change-Id: I8d5fb7fdc4ba5e62d272a5b74466e45e1fc051af
(cherry picked from commit 48ed3547)
Merged-In: I8d5fb7fdc4ba5e62d272a5b74466e45e1fc051af
parent 0be926ff
Loading
Loading
Loading
Loading
+80 −3
Original line number Diff line number Diff line
@@ -122,6 +122,7 @@ import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
import com.android.internal.net.VpnProfile;
import com.android.modules.utils.build.SdkLevel;
import com.android.net.module.util.NetdUtils;
import com.android.net.module.util.NetworkStackConstants;
import com.android.server.DeviceIdleInternal;
@@ -207,7 +208,9 @@ public class Vpn {
    private final Context mUserIdContext;
    @VisibleForTesting final Dependencies mDeps;
    private final NetworkInfo mNetworkInfo;
    @GuardedBy("this")
    private int mLegacyState;
    @GuardedBy("this")
    @VisibleForTesting protected String mPackage;
    private int mOwnerUID;
    private boolean mIsPackageTargetingAtLeastQ;
@@ -245,6 +248,7 @@ public class Vpn {
     * Whether to keep the connection active after rebooting, or upgrading or reinstalling. This
     * only applies to {@link VpnService} connections.
     */
    @GuardedBy("this")
    @VisibleForTesting protected boolean mAlwaysOn = false;

    /**
@@ -252,6 +256,7 @@ public class Vpn {
     * apps can still bypass by choosing explicit networks. Has no effect if {@link mAlwaysOn} is
     * not set. Applies to all types of VPNs.
     */
    @GuardedBy("this")
    @VisibleForTesting protected boolean mLockdown = false;

    /**
@@ -604,7 +609,7 @@ public class Vpn {
    }

    /** Returns the package name that is currently prepared. */
    public String getPackage() {
    public synchronized String getPackage() {
        return mPackage;
    }

@@ -685,6 +690,36 @@ public class Vpn {
        return true;
    }

    private boolean sendEventToVpnManagerApp(@NonNull String category, int errorClass,
            int errorCode, @NonNull final String packageName, @Nullable final String sessionKey,
            @NonNull final VpnProfileState profileState, @Nullable final Network underlyingNetwork,
            @Nullable final NetworkCapabilities nc, @Nullable final LinkProperties lp) {
        final Intent intent = new Intent(VpnManager.ACTION_VPN_MANAGER_EVENT);
        intent.setPackage(packageName);
        intent.addCategory(category);
        intent.putExtra(VpnManager.EXTRA_VPN_PROFILE_STATE, profileState);
        intent.putExtra(VpnManager.EXTRA_SESSION_KEY, sessionKey);
        intent.putExtra(VpnManager.EXTRA_UNDERLYING_NETWORK, underlyingNetwork);
        intent.putExtra(VpnManager.EXTRA_UNDERLYING_NETWORK_CAPABILITIES, nc);
        intent.putExtra(VpnManager.EXTRA_UNDERLYING_LINK_PROPERTIES, lp);
        intent.putExtra(VpnManager.EXTRA_TIMESTAMP_MILLIS, System.currentTimeMillis());
        if (!VpnManager.CATEGORY_EVENT_DEACTIVATED_BY_USER.equals(category)
                || !VpnManager.CATEGORY_EVENT_ALWAYS_ON_STATE_CHANGED.equals(category)) {
            intent.putExtra(VpnManager.EXTRA_ERROR_CLASS, errorClass);
            intent.putExtra(VpnManager.EXTRA_ERROR_CODE, errorCode);
        }
        try {
            return mUserIdContext.startService(intent) != null;
        } catch (RuntimeException e) {
            Log.e(TAG, "Service of VpnManager app " + intent + " failed to start", e);
            return false;
        }
    }

    private boolean isVpnApp(String packageName) {
        return packageName != null && !VpnConfig.LEGACY_VPN.equals(packageName);
    }

    /**
     * Configures an always-on VPN connection through a specific application. This connection is
     * automatically granted and persisted after a reboot.
@@ -707,9 +742,40 @@ public class Vpn {
            boolean lockdown,
            @Nullable List<String> lockdownAllowlist) {
        enforceControlPermissionOrInternalCaller();
        // Store mPackage since it might be reset or might be replaced with the other VPN app.
        final String oldPackage = mPackage;
        final boolean isPackageChanged = !Objects.equals(packageName, oldPackage);
        // TODO: Remove "SdkLevel.isAtLeastT()" check once VpnManagerService is decoupled from
        //  ConnectivityServiceTest.
        // Only notify VPN apps that were already always-on, and only if the always-on provider
        // changed, or the lockdown mode changed.
        final boolean shouldNotifyOldPkg = isVpnApp(oldPackage) && mAlwaysOn
                && (lockdown != mLockdown || isPackageChanged);
        // Also notify the new package if there was a provider change.
        final boolean shouldNotifyNewPkg = isVpnApp(packageName) && isPackageChanged;

        if (setAlwaysOnPackageInternal(packageName, lockdown, lockdownAllowlist)) {
            saveAlwaysOnPackage();
            // TODO(b/230548427): Remove SDK check once VPN related stuff are decoupled from
            //  ConnectivityServiceTest.
            if (shouldNotifyOldPkg && SdkLevel.isAtLeastT()) {
                // If both of shouldNotifyOldPkg & isPackageChanged are true, which means the
                // always-on of old package is disabled or the old package is replaced with the new
                // package. In this case, VpnProfileState should be disconnected.
                sendEventToVpnManagerApp(VpnManager.CATEGORY_EVENT_ALWAYS_ON_STATE_CHANGED,
                        -1 /* errorClass */, -1 /* errorCode*/, oldPackage,
                        null /* sessionKey */, isPackageChanged ? makeDisconnectedVpnProfileState()
                                : makeVpnProfileStateLocked(),
                        null /* underlyingNetwork */, null /* nc */, null /* lp */);
            }
            // TODO(b/230548427): Remove SDK check once VPN related stuff are decoupled from
            //  ConnectivityServiceTest.
            if (shouldNotifyNewPkg && SdkLevel.isAtLeastT()) {
                sendEventToVpnManagerApp(VpnManager.CATEGORY_EVENT_ALWAYS_ON_STATE_CHANGED,
                        -1 /* errorClass */, -1 /* errorCode*/, packageName,
                        getSessionKeyLocked(), makeVpnProfileStateLocked(),
                        null /* underlyingNetwork */, null /* nc */, null /* lp */);
            }
            return true;
        }
        return false;
@@ -1006,6 +1072,7 @@ public class Vpn {
        return true;
    }

    @GuardedBy("this")
    private boolean isCurrentPreparedPackage(String packageName) {
        // 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
@@ -1299,6 +1366,7 @@ public class Vpn {
        return true;
    }

    @GuardedBy("this")
    private void agentConnect() {
        LinkProperties lp = makeLinkProperties();

@@ -1993,6 +2061,7 @@ public class Vpn {
        return isIkev2VpnRunner() ? VpnManager.TYPE_VPN_PLATFORM : VpnManager.TYPE_VPN_LEGACY;
    }

    @GuardedBy("this")
    private void updateAlwaysOnNotification(DetailedState networkState) {
        final boolean visible = (mAlwaysOn && networkState != DetailedState.CONNECTED);

@@ -3497,11 +3566,19 @@ public class Vpn {
        }
    }

    private VpnProfileState makeVpnProfileState() {
    @GuardedBy("this")
    @NonNull
    private VpnProfileState makeVpnProfileStateLocked() {
        return new VpnProfileState(getStateFromLegacyState(mLegacyState),
                isIkev2VpnRunner() ? getSessionKeyLocked() : null, mAlwaysOn, mLockdown);
    }

    @NonNull
    private VpnProfileState makeDisconnectedVpnProfileState() {
        return new VpnProfileState(VpnProfileState.STATE_DISCONNECTED, null /* sessionKey */,
                false /* alwaysOn */, false /* lockdown */);
    }

    /**
     * Retrieve the VpnProfileState for the profile provisioned by the given package.
     *
@@ -3513,7 +3590,7 @@ public class Vpn {
            @NonNull String packageName) {
        requireNonNull(packageName, "No package name provided");
        enforceNotRestrictedUser();
        return isCurrentIkev2VpnLocked(packageName) ? makeVpnProfileState() : null;
        return isCurrentIkev2VpnLocked(packageName) ? makeVpnProfileStateLocked() : null;
    }

    /**