Send VPN manager event when VPN always-on status is changed
Bug: 225010642
Test: atest FrameworksNetTests:VpnTest
Change-Id: I8d5fb7fdc4ba5e62d272a5b74466e45e1fc051af
(cherry picked from commit 48ed354751)
Merged-In: I8d5fb7fdc4ba5e62d272a5b74466e45e1fc051af
This commit is contained in:
@@ -122,6 +122,7 @@ import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
|
|||||||
import com.android.internal.net.LegacyVpnInfo;
|
import com.android.internal.net.LegacyVpnInfo;
|
||||||
import com.android.internal.net.VpnConfig;
|
import com.android.internal.net.VpnConfig;
|
||||||
import com.android.internal.net.VpnProfile;
|
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.NetdUtils;
|
||||||
import com.android.net.module.util.NetworkStackConstants;
|
import com.android.net.module.util.NetworkStackConstants;
|
||||||
import com.android.server.DeviceIdleInternal;
|
import com.android.server.DeviceIdleInternal;
|
||||||
@@ -207,7 +208,9 @@ public class Vpn {
|
|||||||
private final Context mUserIdContext;
|
private final Context mUserIdContext;
|
||||||
@VisibleForTesting final Dependencies mDeps;
|
@VisibleForTesting final Dependencies mDeps;
|
||||||
private final NetworkInfo mNetworkInfo;
|
private final NetworkInfo mNetworkInfo;
|
||||||
|
@GuardedBy("this")
|
||||||
private int mLegacyState;
|
private int mLegacyState;
|
||||||
|
@GuardedBy("this")
|
||||||
@VisibleForTesting protected String mPackage;
|
@VisibleForTesting protected String mPackage;
|
||||||
private int mOwnerUID;
|
private int mOwnerUID;
|
||||||
private boolean mIsPackageTargetingAtLeastQ;
|
private boolean mIsPackageTargetingAtLeastQ;
|
||||||
@@ -245,6 +248,7 @@ public class Vpn {
|
|||||||
* Whether to keep the connection active after rebooting, or upgrading or reinstalling. This
|
* Whether to keep the connection active after rebooting, or upgrading or reinstalling. This
|
||||||
* only applies to {@link VpnService} connections.
|
* only applies to {@link VpnService} connections.
|
||||||
*/
|
*/
|
||||||
|
@GuardedBy("this")
|
||||||
@VisibleForTesting protected boolean mAlwaysOn = false;
|
@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
|
* apps can still bypass by choosing explicit networks. Has no effect if {@link mAlwaysOn} is
|
||||||
* not set. Applies to all types of VPNs.
|
* not set. Applies to all types of VPNs.
|
||||||
*/
|
*/
|
||||||
|
@GuardedBy("this")
|
||||||
@VisibleForTesting protected boolean mLockdown = false;
|
@VisibleForTesting protected boolean mLockdown = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -604,7 +609,7 @@ public class Vpn {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the package name that is currently prepared. */
|
/** Returns the package name that is currently prepared. */
|
||||||
public String getPackage() {
|
public synchronized String getPackage() {
|
||||||
return mPackage;
|
return mPackage;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -685,6 +690,36 @@ public class Vpn {
|
|||||||
return true;
|
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
|
* Configures an always-on VPN connection through a specific application. This connection is
|
||||||
* automatically granted and persisted after a reboot.
|
* automatically granted and persisted after a reboot.
|
||||||
@@ -707,9 +742,40 @@ public class Vpn {
|
|||||||
boolean lockdown,
|
boolean lockdown,
|
||||||
@Nullable List<String> lockdownAllowlist) {
|
@Nullable List<String> lockdownAllowlist) {
|
||||||
enforceControlPermissionOrInternalCaller();
|
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)) {
|
if (setAlwaysOnPackageInternal(packageName, lockdown, lockdownAllowlist)) {
|
||||||
saveAlwaysOnPackage();
|
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 true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -1006,6 +1072,7 @@ public class Vpn {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GuardedBy("this")
|
||||||
private boolean isCurrentPreparedPackage(String packageName) {
|
private boolean isCurrentPreparedPackage(String packageName) {
|
||||||
// We can't just check that packageName matches mPackage, because if the app was uninstalled
|
// 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
|
// 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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GuardedBy("this")
|
||||||
private void agentConnect() {
|
private void agentConnect() {
|
||||||
LinkProperties lp = makeLinkProperties();
|
LinkProperties lp = makeLinkProperties();
|
||||||
|
|
||||||
@@ -1993,6 +2061,7 @@ public class Vpn {
|
|||||||
return isIkev2VpnRunner() ? VpnManager.TYPE_VPN_PLATFORM : VpnManager.TYPE_VPN_LEGACY;
|
return isIkev2VpnRunner() ? VpnManager.TYPE_VPN_PLATFORM : VpnManager.TYPE_VPN_LEGACY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GuardedBy("this")
|
||||||
private void updateAlwaysOnNotification(DetailedState networkState) {
|
private void updateAlwaysOnNotification(DetailedState networkState) {
|
||||||
final boolean visible = (mAlwaysOn && networkState != DetailedState.CONNECTED);
|
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),
|
return new VpnProfileState(getStateFromLegacyState(mLegacyState),
|
||||||
isIkev2VpnRunner() ? getSessionKeyLocked() : null, mAlwaysOn, mLockdown);
|
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.
|
* Retrieve the VpnProfileState for the profile provisioned by the given package.
|
||||||
*
|
*
|
||||||
@@ -3513,7 +3590,7 @@ public class Vpn {
|
|||||||
@NonNull String packageName) {
|
@NonNull String packageName) {
|
||||||
requireNonNull(packageName, "No package name provided");
|
requireNonNull(packageName, "No package name provided");
|
||||||
enforceNotRestrictedUser();
|
enforceNotRestrictedUser();
|
||||||
return isCurrentIkev2VpnLocked(packageName) ? makeVpnProfileState() : null;
|
return isCurrentIkev2VpnLocked(packageName) ? makeVpnProfileStateLocked() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user