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
This commit is contained in:
junyulai
2018-11-12 22:39:30 +08:00
parent 0882543a12
commit efb04d3660
2 changed files with 41 additions and 68 deletions

View File

@@ -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);
}
}
};

View File

@@ -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();
}