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

Commit 4f92a079 authored by Chiachang's avatar Chiachang Committed by Automerger Merge Worker
Browse files

Allow getting/setting app exclusion list for specific vpn profile am: 7d1b14eb

parents 2f22353d 7d1b14eb
Loading
Loading
Loading
Loading
+2 −0
Original line number Original line Diff line number Diff line
@@ -42,6 +42,8 @@ interface IVpnManager {
    String startVpnProfile(String packageName);
    String startVpnProfile(String packageName);
    void stopVpnProfile(String packageName);
    void stopVpnProfile(String packageName);
    VpnProfileState getProvisionedVpnProfileState(String packageName);
    VpnProfileState getProvisionedVpnProfileState(String packageName);
    boolean setAppExclusionList(int userId, String vpnPackage, in List<String> excludedApps);
    List<String> getAppExclusionList(int userId, String vpnPackage);


    /** Always-on VPN APIs */
    /** Always-on VPN APIs */
    boolean isAlwaysOnVpnPackageSupported(int userId, String packageName);
    boolean isAlwaysOnVpnPackageSupported(int userId, String packageName);
+57 −0
Original line number Original line Diff line number Diff line
@@ -594,6 +594,63 @@ public class VpnManager {
        }
        }
    }
    }


    /**
     * Sets the application exclusion list for the specified VPN profile.
     *
     * <p>If an app in the set of excluded apps is not installed for the given user, it will be
     * skipped in the list of app exclusions. If apps are installed or removed, any active VPN will
     * have its UID set updated automatically. If the caller is not {@code userId},
     * {@link android.Manifest.permission.INTERACT_ACROSS_USERS_FULL} permission is required.
     *
     * <p>This will ONLY affect VpnManager profiles. As such, the NETWORK_SETTINGS provider MUST NOT
     * allow configuration of these options if the application has not provided a VPN profile.
     *
     * @param userId the identifier of the user to set app exclusion list
     * @param vpnPackage The package name for an installed VPN app on the device
     * @param excludedApps the app exclusion list
     * @throws IllegalStateException exception if vpn for the @code userId} is not ready yet.
     *
     * @return whether setting the list is successful or not
     * @hide
     */
    @RequiresPermission(anyOf = {
            android.Manifest.permission.NETWORK_SETTINGS,
            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
            android.Manifest.permission.NETWORK_STACK})
    public boolean setAppExclusionList(int userId, @NonNull String vpnPackage,
            @NonNull List<String> excludedApps) {
        try {
            return mService.setAppExclusionList(userId, vpnPackage, excludedApps);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * Gets the application exclusion list for the specified VPN profile. If the caller is not
     * {@code userId}, {@link android.Manifest.permission.INTERACT_ACROSS_USERS_FULL} permission
     * is required.
     *
     * @param userId the identifier of the user to set app exclusion list
     * @param vpnPackage The package name for an installed VPN app on the device
     * @return the list of packages for the specified VPN profile or null if no corresponding VPN
     *         profile configured.
     *
     * @hide
     */
    @RequiresPermission(anyOf = {
            android.Manifest.permission.NETWORK_SETTINGS,
            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
            android.Manifest.permission.NETWORK_STACK})
    @Nullable
    public List<String> getAppExclusionList(int userId, @NonNull String vpnPackage) {
        try {
            return mService.getAppExclusionList(userId, vpnPackage);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
    /**
     * @return the list of packages that are allowed to access network when always-on VPN is in
     * @return the list of packages that are allowed to access network when always-on VPN is in
     * lockdown mode but not connected. Returns {@code null} when VPN lockdown is not active.
     * lockdown mode but not connected. Returns {@code null} when VPN lockdown is not active.
+32 −0
Original line number Original line Diff line number Diff line
@@ -880,6 +880,38 @@ public class VpnManagerService extends IVpnManager.Stub {
        }
        }
    }
    }


    @Override
    public boolean setAppExclusionList(int userId, String vpnPackage, List<String> excludedApps) {
        enforceSettingsPermission();
        enforceCrossUserPermission(userId);

        synchronized (mVpns) {
            final Vpn vpn = mVpns.get(userId);
            if (vpn != null) {
                return vpn.setAppExclusionList(vpnPackage, excludedApps);
            } else {
                logw("User " + userId + " has no Vpn configuration");
                throw new IllegalStateException(
                        "VPN for user " + userId + " not ready yet. Skipping setting the list");
            }
        }
    }

    @Override
    public List<String> getAppExclusionList(int userId, String vpnPackage) {
        enforceSettingsPermission();
        enforceCrossUserPermission(userId);

        synchronized (mVpns) {
            final Vpn vpn = mVpns.get(userId);
            if (vpn != null) {
                return vpn.getAppExclusionList(vpnPackage);
            } else {
                logw("User " + userId + " has no Vpn configuration");
                return null;
            }
        }
    }


    @Override
    @Override
    public void factoryReset() {
    public void factoryReset() {
+90 −0
Original line number Original line Diff line number Diff line
@@ -27,6 +27,8 @@ import static android.net.VpnManager.NOTIFICATION_CHANNEL_VPN;
import static android.os.PowerWhitelistManager.REASON_VPN;
import static android.os.PowerWhitelistManager.REASON_VPN;
import static android.os.UserHandle.PER_USER_RANGE;
import static android.os.UserHandle.PER_USER_RANGE;


import static com.android.server.vcn.util.PersistableBundleUtils.STRING_DESERIALIZER;

import static java.util.Objects.requireNonNull;
import static java.util.Objects.requireNonNull;


import android.Manifest;
import android.Manifest;
@@ -97,6 +99,7 @@ import android.os.INetworkManagementService;
import android.os.Looper;
import android.os.Looper;
import android.os.Parcel;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.os.Process;
import android.os.Process;
import android.os.RemoteException;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemClock;
@@ -127,6 +130,7 @@ import com.android.net.module.util.NetworkStackConstants;
import com.android.server.DeviceIdleInternal;
import com.android.server.DeviceIdleInternal;
import com.android.server.LocalServices;
import com.android.server.LocalServices;
import com.android.server.net.BaseNetworkObserver;
import com.android.server.net.BaseNetworkObserver;
import com.android.server.vcn.util.PersistableBundleUtils;


import libcore.io.IoUtils;
import libcore.io.IoUtils;


@@ -174,6 +178,8 @@ public class Vpn {
    private static final String VPN_PROVIDER_NAME_BASE = "VpnNetworkProvider:";
    private static final String VPN_PROVIDER_NAME_BASE = "VpnNetworkProvider:";
    private static final boolean LOGD = true;
    private static final boolean LOGD = true;
    private static final String ANDROID_KEYSTORE_PROVIDER = "AndroidKeyStore";
    private static final String ANDROID_KEYSTORE_PROVIDER = "AndroidKeyStore";
    /** Key containing prefix of vpn app excluded list */
    @VisibleForTesting static final String VPN_APP_EXCLUDED = "VPN_APP_EXCLUDED_";


    // Length of time (in milliseconds) that an app hosting an always-on VPN is placed on
    // Length of time (in milliseconds) that an app hosting an always-on VPN is placed on
    // the device idle allowlist during service launch and VPN bootstrap.
    // the device idle allowlist during service launch and VPN bootstrap.
@@ -2636,6 +2642,8 @@ public class Vpn {


                    mConfig.underlyingNetworks = new Network[] {network};
                    mConfig.underlyingNetworks = new Network[] {network};


                    mConfig.disallowedApplications = getAppExclusionList(mPackage);

                    networkAgent = mNetworkAgent;
                    networkAgent = mNetworkAgent;


                    // The below must be done atomically with the mConfig update, otherwise
                    // The below must be done atomically with the mConfig update, otherwise
@@ -3486,6 +3494,88 @@ public class Vpn {
        }
        }
    }
    }


    private boolean storeAppExclusionList(@NonNull String packageName,
            @NonNull List<String> excludedApps) {
        byte[] data;
        try {
            final PersistableBundle bundle = PersistableBundleUtils.fromList(
                    excludedApps, PersistableBundleUtils.STRING_SERIALIZER);
            data = PersistableBundleUtils.toDiskStableBytes(bundle);
        } catch (IOException e) {
            Log.e(TAG, "problem writing into stream", e);
            return false;
        }

        final long oldId = Binder.clearCallingIdentity();
        try {
            getVpnProfileStore().put(getVpnAppExcludedForPackage(packageName), data);
        } finally {
            Binder.restoreCallingIdentity(oldId);
        }
        return true;
    }

    @VisibleForTesting
    String getVpnAppExcludedForPackage(String packageName) {
        return VPN_APP_EXCLUDED + mUserId + "_" + packageName;
    }

    /**
     * Set the application exclusion list for the specified VPN profile.
     *
     * @param packageName the package name of the app provisioning this profile
     * @param excludedApps the list of excluded packages
     *
     * @return whether setting the list is successful or not
     */
    public synchronized boolean setAppExclusionList(@NonNull String packageName,
            @NonNull List<String> excludedApps) {
        enforceNotRestrictedUser();
        if (!storeAppExclusionList(packageName, excludedApps)) return false;
        // Re-build and update NetworkCapabilities via NetworkAgent.
        if (mNetworkAgent != null) {
            // Only update the platform VPN
            if (isIkev2VpnRunner()) {
                mConfig.disallowedApplications = List.copyOf(excludedApps);
                mNetworkCapabilities = new NetworkCapabilities.Builder(mNetworkCapabilities)
                        .setUids(createUserAndRestrictedProfilesRanges(
                                mUserId, null /* allowedApplications */, excludedApps))
                        .build();
                mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
            }
        }

        return true;
    }

    /**
     * Gets the application exclusion list for the specified VPN profile.
     *
     * @param packageName the package name of the app provisioning this profile
     * @return the list of excluded packages for the specified VPN profile or empty list if there is
     *         no provisioned VPN profile.
     */
    @NonNull
    public synchronized List<String> getAppExclusionList(@NonNull String packageName) {
        enforceNotRestrictedUser();

        final long oldId = Binder.clearCallingIdentity();
        try {
            final byte[] bytes = getVpnProfileStore().get(getVpnAppExcludedForPackage(packageName));

            if (bytes == null || bytes.length == 0) return new ArrayList<>();

            final PersistableBundle bundle = PersistableBundleUtils.fromDiskStableBytes(bytes);
            return PersistableBundleUtils.toList(bundle, STRING_DESERIALIZER);
        } catch (IOException e) {
            Log.e(TAG, "problem reading from stream", e);
        }  finally {
            Binder.restoreCallingIdentity(oldId);
        }

        return new ArrayList<>();
    }

    private @VpnProfileState.State int getStateFromLegacyState(int legacyState) {
    private @VpnProfileState.State int getStateFromLegacyState(int legacyState) {
        switch (legacyState) {
        switch (legacyState) {
            case LegacyVpnInfo.STATE_CONNECTING:
            case LegacyVpnInfo.STATE_CONNECTING: