Allow getting/setting app exclusion list for specific vpn profile am: 7d1b14eb1f
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/18218767 Change-Id: Ia1b5af68ab379636d3e6fa9441df994b2e7d4416 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -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);
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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() {
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
Reference in New Issue
Block a user