Merge changes from topic "VPN_APPOPS"

* changes:
  Add startOp and finishOp to track the behavior of VPN app
  Add new appOps to track the behavior of VPN app
This commit is contained in:
Lucas Lin
2021-11-01 08:33:36 +00:00
committed by Gerrit Code Review
3 changed files with 124 additions and 16 deletions

View File

@@ -475,6 +475,8 @@ package android.app {
field public static final String OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED = "android:auto_revoke_permissions_if_unused"; field public static final String OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED = "android:auto_revoke_permissions_if_unused";
field public static final String OPSTR_BIND_ACCESSIBILITY_SERVICE = "android:bind_accessibility_service"; field public static final String OPSTR_BIND_ACCESSIBILITY_SERVICE = "android:bind_accessibility_service";
field public static final String OPSTR_CHANGE_WIFI_STATE = "android:change_wifi_state"; field public static final String OPSTR_CHANGE_WIFI_STATE = "android:change_wifi_state";
field public static final String OPSTR_ESTABLISH_VPN_MANAGER = "android:establish_vpn_manager";
field public static final String OPSTR_ESTABLISH_VPN_SERVICE = "android:establish_vpn_service";
field public static final String OPSTR_GET_ACCOUNTS = "android:get_accounts"; field public static final String OPSTR_GET_ACCOUNTS = "android:get_accounts";
field public static final String OPSTR_GPS = "android:gps"; field public static final String OPSTR_GPS = "android:gps";
field public static final String OPSTR_INSTANT_APP_START_FOREGROUND = "android:instant_app_start_foreground"; field public static final String OPSTR_INSTANT_APP_START_FOREGROUND = "android:instant_app_start_foreground";

View File

@@ -1313,9 +1313,23 @@ public class AppOpsManager {
public static final int OP_RECORD_INCOMING_PHONE_AUDIO = public static final int OP_RECORD_INCOMING_PHONE_AUDIO =
AppProtoEnums.APP_OP_RECORD_INCOMING_PHONE_AUDIO; AppProtoEnums.APP_OP_RECORD_INCOMING_PHONE_AUDIO;
/**
* VPN app establishes a connection through the VpnService API.
*
* @hide
*/
public static final int OP_ESTABLISH_VPN_SERVICE = AppProtoEnums.APP_OP_ESTABLISH_VPN_SERVICE;
/**
* VPN app establishes a connection through the VpnManager API.
*
* @hide
*/
public static final int OP_ESTABLISH_VPN_MANAGER = AppProtoEnums.APP_OP_ESTABLISH_VPN_MANAGER;
/** @hide */ /** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public static final int _NUM_OP = 117; public static final int _NUM_OP = 119;
/** Access to coarse location information. */ /** Access to coarse location information. */
public static final String OPSTR_COARSE_LOCATION = "android:coarse_location"; public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -1754,6 +1768,22 @@ public class AppOpsManager {
public static final String OPSTR_RECORD_INCOMING_PHONE_AUDIO = public static final String OPSTR_RECORD_INCOMING_PHONE_AUDIO =
"android:record_incoming_phone_audio"; "android:record_incoming_phone_audio";
/**
* VPN app establishes a connection through the VpnService API.
*
* @hide
*/
@SystemApi
public static final String OPSTR_ESTABLISH_VPN_SERVICE = "android:establish_vpn_service";
/**
* VPN app establishes a connection through the VpnManager API.
*
* @hide
*/
@SystemApi
public static final String OPSTR_ESTABLISH_VPN_MANAGER = "android:establish_vpn_manager";
/** {@link #sAppOpsToNote} not initialized yet for this op */ /** {@link #sAppOpsToNote} not initialized yet for this op */
private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0; private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0;
/** Should not collect noting of this app-op in {@link #sAppOpsToNote} */ /** Should not collect noting of this app-op in {@link #sAppOpsToNote} */
@@ -1970,6 +2000,8 @@ public class AppOpsManager {
OP_BLUETOOTH_ADVERTISE, // OP_BLUETOOTH_ADVERTISE OP_BLUETOOTH_ADVERTISE, // OP_BLUETOOTH_ADVERTISE
OP_RECORD_INCOMING_PHONE_AUDIO, // OP_RECORD_INCOMING_PHONE_AUDIO OP_RECORD_INCOMING_PHONE_AUDIO, // OP_RECORD_INCOMING_PHONE_AUDIO
OP_NEARBY_WIFI_DEVICES, // OP_NEARBY_WIFI_DEVICES OP_NEARBY_WIFI_DEVICES, // OP_NEARBY_WIFI_DEVICES
OP_ESTABLISH_VPN_SERVICE, // OP_ESTABLISH_VPN_SERVICE
OP_ESTABLISH_VPN_MANAGER, // OP_ESTABLISH_VPN_MANAGER
}; };
/** /**
@@ -2093,6 +2125,8 @@ public class AppOpsManager {
OPSTR_BLUETOOTH_ADVERTISE, OPSTR_BLUETOOTH_ADVERTISE,
OPSTR_RECORD_INCOMING_PHONE_AUDIO, OPSTR_RECORD_INCOMING_PHONE_AUDIO,
OPSTR_NEARBY_WIFI_DEVICES, OPSTR_NEARBY_WIFI_DEVICES,
OPSTR_ESTABLISH_VPN_SERVICE,
OPSTR_ESTABLISH_VPN_MANAGER,
}; };
/** /**
@@ -2216,7 +2250,9 @@ public class AppOpsManager {
"ACTIVITY_RECOGNITION_SOURCE", "ACTIVITY_RECOGNITION_SOURCE",
"BLUETOOTH_ADVERTISE", "BLUETOOTH_ADVERTISE",
"RECORD_INCOMING_PHONE_AUDIO", "RECORD_INCOMING_PHONE_AUDIO",
"NEARBY_WIFI_DEVICES" "NEARBY_WIFI_DEVICES",
"ESTABLISH_VPN_SERVICE",
"ESTABLISH_VPN_MANAGER",
}; };
/** /**
@@ -2342,6 +2378,8 @@ public class AppOpsManager {
Manifest.permission.BLUETOOTH_ADVERTISE, Manifest.permission.BLUETOOTH_ADVERTISE,
null, // no permission for OP_RECORD_INCOMING_PHONE_AUDIO, null, // no permission for OP_RECORD_INCOMING_PHONE_AUDIO,
Manifest.permission.NEARBY_WIFI_DEVICES, Manifest.permission.NEARBY_WIFI_DEVICES,
null, // no permission for OP_ESTABLISH_VPN_SERVICE
null, // no permission for OP_ESTABLISH_VPN_MANAGER
}; };
/** /**
@@ -2467,6 +2505,8 @@ public class AppOpsManager {
null, // BLUETOOTH_ADVERTISE null, // BLUETOOTH_ADVERTISE
null, // RECORD_INCOMING_PHONE_AUDIO null, // RECORD_INCOMING_PHONE_AUDIO
null, // NEARBY_WIFI_DEVICES null, // NEARBY_WIFI_DEVICES
null, // ESTABLISH_VPN_SERVICE
null, // ESTABLISH_VPN_MANAGER
}; };
/** /**
@@ -2591,6 +2631,8 @@ public class AppOpsManager {
null, // BLUETOOTH_ADVERTISE null, // BLUETOOTH_ADVERTISE
null, // RECORD_INCOMING_PHONE_AUDIO null, // RECORD_INCOMING_PHONE_AUDIO
null, // NEARBY_WIFI_DEVICES null, // NEARBY_WIFI_DEVICES
null, // ESTABLISH_VPN_SERVICE
null, // ESTABLISH_VPN_MANAGER
}; };
/** /**
@@ -2714,6 +2756,8 @@ public class AppOpsManager {
AppOpsManager.MODE_ALLOWED, // BLUETOOTH_ADVERTISE AppOpsManager.MODE_ALLOWED, // BLUETOOTH_ADVERTISE
AppOpsManager.MODE_ALLOWED, // RECORD_INCOMING_PHONE_AUDIO AppOpsManager.MODE_ALLOWED, // RECORD_INCOMING_PHONE_AUDIO
AppOpsManager.MODE_ALLOWED, // NEARBY_WIFI_DEVICES AppOpsManager.MODE_ALLOWED, // NEARBY_WIFI_DEVICES
AppOpsManager.MODE_ALLOWED, // ESTABLISH_VPN_SERVICE
AppOpsManager.MODE_ALLOWED, // ESTABLISH_VPN_MANAGER
}; };
/** /**
@@ -2841,6 +2885,8 @@ public class AppOpsManager {
false, // BLUETOOTH_ADVERTISE false, // BLUETOOTH_ADVERTISE
false, // RECORD_INCOMING_PHONE_AUDIO false, // RECORD_INCOMING_PHONE_AUDIO
false, // NEARBY_WIFI_DEVICES false, // NEARBY_WIFI_DEVICES
false, // OP_ESTABLISH_VPN_SERVICE
false, // OP_ESTABLISH_VPN_MANAGER
}; };
/** /**

View File

@@ -126,6 +126,7 @@ import com.android.server.net.BaseNetworkObserver;
import libcore.io.IoUtils; import libcore.io.IoUtils;
import java.io.File; import java.io.File;
import java.io.FileDescriptor;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
@@ -195,6 +196,7 @@ public class Vpn {
private final Context mContext; private final Context mContext;
private final ConnectivityManager mConnectivityManager; private final ConnectivityManager mConnectivityManager;
private final AppOpsManager mAppOpsManager;
// The context is for specific user which is created from mUserId // The context is for specific user which is created from mUserId
private final Context mUserIdContext; private final Context mUserIdContext;
@VisibleForTesting final Dependencies mDeps; @VisibleForTesting final Dependencies mDeps;
@@ -407,6 +409,46 @@ public class Vpn {
public boolean isInterfacePresent(final Vpn vpn, final String iface) { public boolean isInterfacePresent(final Vpn vpn, final String iface) {
return vpn.jniCheck(iface) != 0; return vpn.jniCheck(iface) != 0;
} }
/**
* @see ParcelFileDescriptor#adoptFd(int)
*/
public ParcelFileDescriptor adoptFd(Vpn vpn, int mtu) {
return ParcelFileDescriptor.adoptFd(jniCreate(vpn, mtu));
}
/**
* Call native method to create the VPN interface and return the FileDescriptor of /dev/tun.
*/
public int jniCreate(Vpn vpn, int mtu) {
return vpn.jniCreate(mtu);
}
/**
* Call native method to get the interface name of VPN.
*/
public String jniGetName(Vpn vpn, int fd) {
return vpn.jniGetName(fd);
}
/**
* Call native method to set the VPN addresses and return the number of addresses.
*/
public int jniSetAddresses(Vpn vpn, String interfaze, String addresses) {
return vpn.jniSetAddresses(interfaze, addresses);
}
/**
* @see IoUtils#setBlocking(FileDescriptor, boolean)
*/
public void setBlocking(FileDescriptor fd, boolean blocking) {
try {
IoUtils.setBlocking(fd, blocking);
} catch (IOException e) {
throw new IllegalStateException(
"Cannot set tunnel's fd as blocking=" + blocking, e);
}
}
} }
public Vpn(Looper looper, Context context, INetworkManagementService netService, INetd netd, public Vpn(Looper looper, Context context, INetworkManagementService netService, INetd netd,
@@ -431,6 +473,7 @@ public class Vpn {
mVpnProfileStore = vpnProfileStore; mVpnProfileStore = vpnProfileStore;
mContext = context; mContext = context;
mConnectivityManager = mContext.getSystemService(ConnectivityManager.class); mConnectivityManager = mContext.getSystemService(ConnectivityManager.class);
mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
mUserIdContext = context.createContextAsUser(UserHandle.of(userId), 0 /* flags */); mUserIdContext = context.createContextAsUser(UserHandle.of(userId), 0 /* flags */);
mDeps = deps; mDeps = deps;
mNms = netService; mNms = netService;
@@ -826,7 +869,6 @@ public class Vpn {
VpnProfile profile = getVpnProfilePrivileged(alwaysOnPackage); VpnProfile profile = getVpnProfilePrivileged(alwaysOnPackage);
if (profile != null) { if (profile != null) {
startVpnProfilePrivileged(profile, alwaysOnPackage); startVpnProfilePrivileged(profile, alwaysOnPackage);
// If the above startVpnProfilePrivileged() call returns, the Ikev2VpnProfile was // If the above startVpnProfilePrivileged() call returns, the Ikev2VpnProfile was
// correctly parsed, and the VPN has started running in a different thread. The only // correctly parsed, and the VPN has started running in a different thread. The only
// other possibility is that the above call threw an exception, which will be // other possibility is that the above call threw an exception, which will be
@@ -974,9 +1016,15 @@ public class Vpn {
} catch (Exception e) { } catch (Exception e) {
// ignore // ignore
} }
mAppOpsManager.finishOp(
AppOpsManager.OPSTR_ESTABLISH_VPN_SERVICE, mOwnerUID, mPackage, null);
mContext.unbindService(mConnection); mContext.unbindService(mConnection);
cleanupVpnStateLocked(); cleanupVpnStateLocked();
} else if (mVpnRunner != null) { } else if (mVpnRunner != null) {
if (!VpnConfig.LEGACY_VPN.equals(mPackage)) {
mAppOpsManager.finishOp(
AppOpsManager.OPSTR_ESTABLISH_VPN_MANAGER, mOwnerUID, mPackage, null);
}
// cleanupVpnStateLocked() is called from mVpnRunner.exit() // cleanupVpnStateLocked() is called from mVpnRunner.exit()
mVpnRunner.exit(); mVpnRunner.exit();
} }
@@ -1041,10 +1089,8 @@ public class Vpn {
return false; return false;
} }
final AppOpsManager appOpMgr =
(AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
for (final String appOpStr : toChange) { for (final String appOpStr : toChange) {
appOpMgr.setMode( mAppOpsManager.setMode(
appOpStr, appOpStr,
uid, uid,
packageName, packageName,
@@ -1366,9 +1412,9 @@ public class Vpn {
Set<Range<Integer>> oldUsers = mNetworkCapabilities.getUids(); Set<Range<Integer>> oldUsers = mNetworkCapabilities.getUids();
// Configure the interface. Abort if any of these steps fails. // Configure the interface. Abort if any of these steps fails.
ParcelFileDescriptor tun = ParcelFileDescriptor.adoptFd(jniCreate(config.mtu)); final ParcelFileDescriptor tun = mDeps.adoptFd(this, config.mtu);
try { try {
String interfaze = jniGetName(tun.getFd()); final String interfaze = mDeps.jniGetName(this, tun.getFd());
// TEMP use the old jni calls until there is support for netd address setting // TEMP use the old jni calls until there is support for netd address setting
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
@@ -1376,7 +1422,7 @@ public class Vpn {
builder.append(" "); builder.append(" ");
builder.append(address); builder.append(address);
} }
if (jniSetAddresses(interfaze, builder.toString()) < 1) { if (mDeps.jniSetAddresses(this, interfaze, builder.toString()) < 1) {
throw new IllegalArgumentException("At least one address must be specified"); throw new IllegalArgumentException("At least one address must be specified");
} }
Connection connection = new Connection(); Connection connection = new Connection();
@@ -1422,11 +1468,11 @@ public class Vpn {
jniReset(oldInterface); jniReset(oldInterface);
} }
try { mDeps.setBlocking(tun.getFileDescriptor(), config.blocking);
IoUtils.setBlocking(tun.getFileDescriptor(), config.blocking); // Record that the VPN connection is established by an app which uses VpnService API.
} catch (IOException e) { if (oldNetworkAgent != mNetworkAgent) {
throw new IllegalStateException( mAppOpsManager.startOp(
"Cannot set tunnel's fd as blocking=" + config.blocking, e); AppOpsManager.OPSTR_ESTABLISH_VPN_SERVICE, mOwnerUID, mPackage, null, null);
} }
} catch (RuntimeException e) { } catch (RuntimeException e) {
IoUtils.closeQuietly(tun); IoUtils.closeQuietly(tun);
@@ -1781,9 +1827,17 @@ public class Vpn {
synchronized (Vpn.this) { synchronized (Vpn.this) {
if (interfaze.equals(mInterface) && jniCheck(interfaze) == 0) { if (interfaze.equals(mInterface) && jniCheck(interfaze) == 0) {
if (mConnection != null) { if (mConnection != null) {
mAppOpsManager.finishOp(
AppOpsManager.OPSTR_ESTABLISH_VPN_SERVICE, mOwnerUID, mPackage,
null);
mContext.unbindService(mConnection); mContext.unbindService(mConnection);
cleanupVpnStateLocked(); cleanupVpnStateLocked();
} else if (mVpnRunner != null) { } else if (mVpnRunner != null) {
if (!VpnConfig.LEGACY_VPN.equals(mPackage)) {
mAppOpsManager.finishOp(
AppOpsManager.OPSTR_ESTABLISH_VPN_MANAGER, mOwnerUID, mPackage,
null);
}
// cleanupVpnStateLocked() is called from mVpnRunner.exit() // cleanupVpnStateLocked() is called from mVpnRunner.exit()
mVpnRunner.exit(); mVpnRunner.exit();
} }
@@ -3254,8 +3308,7 @@ public class Vpn {
* *
* @param packageName the package name of the app provisioning this profile * @param packageName the package name of the app provisioning this profile
*/ */
public synchronized void startVpnProfile( public synchronized void startVpnProfile(@NonNull String packageName) {
@NonNull String packageName) {
requireNonNull(packageName, "No package name provided"); requireNonNull(packageName, "No package name provided");
enforceNotRestrictedUser(); enforceNotRestrictedUser();
@@ -3318,6 +3371,13 @@ public class Vpn {
Log.d(TAG, "Unknown VPN profile type: " + profile.type); Log.d(TAG, "Unknown VPN profile type: " + profile.type);
break; break;
} }
// Record that the VPN connection is established by an app which uses VpnManager API.
if (!VpnConfig.LEGACY_VPN.equals(packageName)) {
mAppOpsManager.startOp(
AppOpsManager.OPSTR_ESTABLISH_VPN_MANAGER, mOwnerUID, mPackage, null,
null);
}
} catch (GeneralSecurityException e) { } catch (GeneralSecurityException e) {
// Reset mConfig // Reset mConfig
mConfig = null; mConfig = null;