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

Commit efb04d36 authored by junyulai's avatar junyulai
Browse files

VPN: Move package intent receiver to ConnectivityService.

Currently, PermissionMonitor listen to user add/remove and
package add/remove intent respectively, and so does VPN.
Thus, races might occurr between them.

This commit refactor VPN part by using ConnectivityService to
listen to intents and dispatch events to VPN.

Bug: 118811303
Test: 1. atest FrameworksNetTests
      2. manually add/remove package
      3. cts-tradefed run cts -m CtsHostsideNetworkTests

Change-Id: Id76fd77c5fcfb2b0e21f211f63f007b1ea1aa53f
parent 0882543a
Loading
Loading
Loading
Loading
+41 −2
Original line number Diff line number Diff line
@@ -902,6 +902,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
        // Listen to package add and removal events for all users.
        intentFilter = new IntentFilter();
        intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
        intentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
        intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
        intentFilter.addDataScheme("package");
        mContext.registerReceiverAsUser(
@@ -4203,12 +4204,46 @@ public class ConnectivityService extends IConnectivityManager.Stub
        mPermissionMonitor.onPackageAdded(packageName, uid);
    }

    private void onPackageRemoved(String packageName, int uid) {
    private void onPackageReplaced(String packageName, int uid) {
        if (TextUtils.isEmpty(packageName) || uid < 0) {
            Slog.wtf(TAG, "Invalid package in onPackageReplaced: " + packageName + " | " + uid);
            return;
        }
        final int userId = UserHandle.getUserId(uid);
        synchronized (mVpns) {
            final Vpn vpn = mVpns.get(userId);
            if (vpn == null) {
                return;
            }
            // Legacy always-on VPN won't be affected since the package name is not set.
            if (TextUtils.equals(vpn.getAlwaysOnPackage(), packageName)) {
                Slog.d(TAG, "Restarting always-on VPN package " + packageName + " for user "
                        + userId);
                vpn.startAlwaysOnVpn();
            }
        }
    }

    private void onPackageRemoved(String packageName, int uid, boolean isReplacing) {
        if (TextUtils.isEmpty(packageName) || uid < 0) {
            Slog.wtf(TAG, "Invalid package in onPackageRemoved: " + packageName + " | " + uid);
            return;
        }
        mPermissionMonitor.onPackageRemoved(uid);

        final int userId = UserHandle.getUserId(uid);
        synchronized (mVpns) {
            final Vpn vpn = mVpns.get(userId);
            if (vpn == null) {
                return;
            }
            // Legacy always-on VPN won't be affected since the package name is not set.
            if (TextUtils.equals(vpn.getAlwaysOnPackage(), packageName) && !isReplacing) {
                Slog.d(TAG, "Removing always-on VPN package " + packageName + " for user "
                        + userId);
                vpn.setAlwaysOnPackage(null, false);
            }
        }
    }

    private void onUserUnlocked(int userId) {
@@ -4245,8 +4280,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
                onUserUnlocked(userId);
            } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
                onPackageAdded(packageName, uid);
            } else if (Intent.ACTION_PACKAGE_REPLACED.equals(action)) {
                onPackageReplaced(packageName, uid);
            } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
                onPackageRemoved(packageName, uid);
                final boolean isReplacing = intent.getBooleanExtra(
                        Intent.EXTRA_REPLACING, false);
                onPackageRemoved(packageName, uid, isReplacing);
            }
        }
    };
+0 −66
Original line number Diff line number Diff line
@@ -206,45 +206,6 @@ public class Vpn {
    // Handle of the user initiating VPN.
    private final int mUserHandle;

    // Listen to package removal and change events (update/uninstall) for this user
    private final BroadcastReceiver mPackageIntentReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            final Uri data = intent.getData();
            final String packageName = data == null ? null : data.getSchemeSpecificPart();
            if (packageName == null) {
                return;
            }

            synchronized (Vpn.this) {
                // Avoid race where always-on package has been unset
                if (!packageName.equals(getAlwaysOnPackage())) {
                    return;
                }

                final String action = intent.getAction();
                Log.i(TAG, "Received broadcast " + action + " for always-on VPN package "
                        + packageName + " in user " + mUserHandle);

                switch(action) {
                    case Intent.ACTION_PACKAGE_REPLACED:
                        // Start vpn after app upgrade
                        startAlwaysOnVpn();
                        break;
                    case Intent.ACTION_PACKAGE_REMOVED:
                        final boolean isPackageRemoved = !intent.getBooleanExtra(
                                Intent.EXTRA_REPLACING, false);
                        if (isPackageRemoved) {
                            setAlwaysOnPackage(null, false);
                        }
                        break;
                }
            }
        }
    };

    private boolean mIsPackageIntentReceiverRegistered = false;

    public Vpn(Looper looper, Context context, INetworkManagementService netService,
            @UserIdInt int userHandle) {
        this(looper, context, netService, userHandle, new SystemServices(context));
@@ -500,7 +461,6 @@ public class Vpn {
            // Prepare this app. The notification will update as a side-effect of updateState().
            prepareInternal(packageName);
        }
        maybeRegisterPackageChangeReceiverLocked(packageName);
        setVpnForcedLocked(mLockdown);
        return true;
    }
@@ -509,31 +469,6 @@ public class Vpn {
        return packageName == null || VpnConfig.LEGACY_VPN.equals(packageName);
    }

    private void unregisterPackageChangeReceiverLocked() {
        if (mIsPackageIntentReceiverRegistered) {
            mContext.unregisterReceiver(mPackageIntentReceiver);
            mIsPackageIntentReceiverRegistered = false;
        }
    }

    private void maybeRegisterPackageChangeReceiverLocked(String packageName) {
        // Unregister IntentFilter listening for previous always-on package change
        unregisterPackageChangeReceiverLocked();

        if (!isNullOrLegacyVpn(packageName)) {
            mIsPackageIntentReceiverRegistered = true;

            IntentFilter intentFilter = new IntentFilter();
            // Protected intent can only be sent by system. No permission required in register.
            intentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
            intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
            intentFilter.addDataScheme("package");
            intentFilter.addDataSchemeSpecificPart(packageName, PatternMatcher.PATTERN_LITERAL);
            mContext.registerReceiverAsUser(
                    mPackageIntentReceiver, UserHandle.of(mUserHandle), intentFilter, null, null);
        }
    }

    /**
     * @return the package name of the VPN controller responsible for always-on VPN,
     *         or {@code null} if none is set or always-on VPN is controlled through
@@ -1302,7 +1237,6 @@ public class Vpn {
        setLockdown(false);
        mAlwaysOn = false;

        unregisterPackageChangeReceiverLocked();
        // Quit any active connections
        agentDisconnect();
    }