Add a skeleton VpnManagerService, and start it on boot.

This adds a lot of unused code but this should make it easier to
review subsequent CLs.

Bug: 173331190
Test: builds, boots, "dumpsys vpnmanager" succeeds
Change-Id: Ied379654a0c3ab6242d3125661fe30f322395059
This commit is contained in:
Lorenzo Colitti
2020-11-09 10:34:03 +09:00
parent 8e40a0623c
commit f83d22b77d
8 changed files with 258 additions and 8 deletions

View File

@@ -0,0 +1,24 @@
/**
* Copyright (c) 2020, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.net;
/**
* Interface that manages VPNs.
*/
/** {@hide} */
interface IVpnManager {
}

View File

@@ -76,6 +76,12 @@ public class VpnManager {
@Deprecated
public static final int TYPE_VPN_LEGACY = 3;
/**
* Channel for VPN notifications.
* @hide
*/
public static final String NOTIFICATION_CHANNEL_VPN = "VPN";
/** @hide */
@IntDef(value = {TYPE_VPN_NONE, TYPE_VPN_SERVICE, TYPE_VPN_PLATFORM, TYPE_VPN_LEGACY})
@Retention(RetentionPolicy.SOURCE)

View File

@@ -207,8 +207,5 @@ filegroup {
"java/com/android/server/connectivity/QosCallbackAgentConnection.java",
"java/com/android/server/connectivity/QosCallbackTracker.java",
"java/com/android/server/connectivity/TcpKeepaliveController.java",
"java/com/android/server/connectivity/Vpn.java",
"java/com/android/server/connectivity/VpnIkev2Utils.java",
"java/com/android/server/net/LockdownVpnTracker.java",
],
}

View File

@@ -0,0 +1,199 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.INetd;
import android.net.IVpnManager;
import android.net.NetworkStack;
import android.net.util.NetdService;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.INetworkManagementService;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.security.KeyStore;
import android.util.Log;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DumpUtils;
import com.android.server.connectivity.Vpn;
import java.io.FileDescriptor;
import java.io.PrintWriter;
/**
* Service that tracks and manages VPNs, and backs the VpnService and VpnManager APIs.
* @hide
*/
public class VpnManagerService extends IVpnManager.Stub {
private static final String TAG = VpnManagerService.class.getSimpleName();
@VisibleForTesting
protected final HandlerThread mHandlerThread;
private final Handler mHandler;
private final Context mContext;
private final Context mUserAllContext;
private final Dependencies mDeps;
private final ConnectivityManager mCm;
private final KeyStore mKeyStore;
private final INetworkManagementService mNMS;
private final INetd mNetd;
private final UserManager mUserManager;
@VisibleForTesting
@GuardedBy("mVpns")
protected final SparseArray<Vpn> mVpns = new SparseArray<>();
/**
* Dependencies of VpnManager, for injection in tests.
*/
@VisibleForTesting
public static class Dependencies {
/** Returns the calling UID of an IPC. */
public int getCallingUid() {
return Binder.getCallingUid();
}
/** Creates a HandlerThread to be used by this class. */
public HandlerThread makeHandlerThread() {
return new HandlerThread("VpnManagerService");
}
/** Returns the KeyStore instance to be used by this class. */
public KeyStore getKeyStore() {
return KeyStore.getInstance();
}
public INetd getNetd() {
return NetdService.getInstance();
}
public INetworkManagementService getINetworkManagementService() {
return INetworkManagementService.Stub.asInterface(
ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
}
}
public VpnManagerService(Context context, Dependencies deps) {
mContext = context;
mDeps = deps;
mHandlerThread = mDeps.makeHandlerThread();
mHandlerThread.start();
mHandler = mHandlerThread.getThreadHandler();
mKeyStore = mDeps.getKeyStore();
mUserAllContext = mContext.createContextAsUser(UserHandle.ALL, 0 /* flags */);
mCm = mContext.getSystemService(ConnectivityManager.class);
mNMS = mDeps.getINetworkManagementService();
mNetd = mDeps.getNetd();
mUserManager = mContext.getSystemService(UserManager.class);
log("VpnManagerService starting up");
}
/** Creates a new VpnManagerService */
public static VpnManagerService create(Context context) {
return new VpnManagerService(context, new Dependencies());
}
/** Informs the service that the system is ready. */
public void systemReady() {
}
@Override
/** Dumps service state. */
protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw,
@Nullable String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
}
private void ensureRunningOnHandlerThread() {
if (mHandler.getLooper().getThread() != Thread.currentThread()) {
throw new IllegalStateException(
"Not running on ConnectivityService thread: "
+ Thread.currentThread().getName());
}
}
private boolean checkAnyPermissionOf(String... permissions) {
for (String permission : permissions) {
if (mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED) {
return true;
}
}
return false;
}
private void enforceAnyPermissionOf(String... permissions) {
if (!checkAnyPermissionOf(permissions)) {
throw new SecurityException("Requires one of the following permissions: "
+ String.join(", ", permissions) + ".");
}
}
private void enforceControlAlwaysOnVpnPermission() {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CONTROL_ALWAYS_ON_VPN,
"ConnectivityService");
}
/**
* Require that the caller is either in the same user or has appropriate permission to interact
* across users.
*
* @param userId Target user for whatever operation the current IPC is supposed to perform.
*/
private void enforceCrossUserPermission(int userId) {
if (userId == UserHandle.getCallingUserId()) {
// Not a cross-user call.
return;
}
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
"VpnManagerService");
}
private void enforceSettingsPermission() {
enforceAnyPermissionOf(
android.Manifest.permission.NETWORK_SETTINGS,
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
}
private static void log(String s) {
Log.d(TAG, s);
}
private static void logw(String s) {
Log.w(TAG, s);
}
private static void loge(String s) {
Log.e(TAG, s);
}
}

View File

@@ -79,7 +79,6 @@ public class NetworkNotificationManager {
// server.
public static final String NOTIFICATION_CHANNEL_NETWORK_STATUS = "NETWORK_STATUS";
public static final String NOTIFICATION_CHANNEL_NETWORK_ALERTS = "NETWORK_ALERTS";
public static final String NOTIFICATION_CHANNEL_VPN = "VPN";
// The context is for the current user (system server)
private final Context mContext;

View File

@@ -21,10 +21,10 @@ import static android.net.ConnectivityManager.NETID_UNSET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.RouteInfo.RTN_THROW;
import static android.net.RouteInfo.RTN_UNREACHABLE;
import static android.net.VpnManager.NOTIFICATION_CHANNEL_VPN;
import static com.android.internal.util.Preconditions.checkArgument;
import static com.android.internal.util.Preconditions.checkNotNull;
import static com.android.server.connectivity.NetworkNotificationManager.NOTIFICATION_CHANNEL_VPN;
import android.Manifest;
import android.annotation.NonNull;
@@ -172,6 +172,12 @@ public class Vpn {
*/
@VisibleForTesting static final int MAX_VPN_PROFILE_SIZE_BYTES = 1 << 17; // 128kB
/**
* Network score that VPNs will announce to ConnectivityService.
* TODO: remove when the network scoring refactor lands.
*/
private static final int VPN_DEFAULT_SCORE = 101;
// TODO: create separate trackers for each unique VPN to support
// automated reconnection
@@ -1233,8 +1239,7 @@ public class Vpn {
}
mNetworkAgent = new NetworkAgent(mContext, mLooper, NETWORKTYPE /* logtag */,
mNetworkCapabilities, lp,
ConnectivityConstants.VPN_DEFAULT_SCORE, networkAgentConfig, mNetworkProvider) {
mNetworkCapabilities, lp, VPN_DEFAULT_SCORE, networkAgentConfig, mNetworkProvider) {
@Override
public void unwanted() {
// We are user controlled, not driven by NetworkRequest.

View File

@@ -18,9 +18,9 @@ package com.android.server.net;
import static android.net.ConnectivityManager.TYPE_NONE;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.VpnManager.NOTIFICATION_CHANNEL_VPN;
import static android.provider.Settings.ACTION_VPN_SETTINGS;
import static com.android.server.connectivity.NetworkNotificationManager.NOTIFICATION_CHANNEL_VPN;
import android.annotation.NonNull;
import android.annotation.Nullable;

View File

@@ -1104,6 +1104,7 @@ public final class SystemServer {
IStorageManager storageManager = null;
NetworkManagementService networkManagement = null;
IpSecService ipSecService = null;
VpnManagerService vpnManager = null;
VcnManagementService vcnManagement = null;
NetworkStatsService networkStats = null;
NetworkPolicyManagerService networkPolicy = null;
@@ -1637,6 +1638,15 @@ public final class SystemServer {
networkPolicy.bindConnectivityManager(connectivity);
t.traceEnd();
t.traceBegin("StartVpnManagerService");
try {
vpnManager = VpnManagerService.create(context);
ServiceManager.addService(Context.VPN_MANAGEMENT_SERVICE, vpnManager);
} catch (Throwable e) {
reportWtf("starting VPN Manager Service", e);
}
t.traceEnd();
t.traceBegin("StartVcnManagementService");
try {
vcnManagement = VcnManagementService.create(context);
@@ -2338,6 +2348,7 @@ public final class SystemServer {
final MediaRouterService mediaRouterF = mediaRouter;
final MmsServiceBroker mmsServiceF = mmsService;
final IpSecService ipSecServiceF = ipSecService;
final VpnManagerService vpnManagerF = vpnManager;
final VcnManagementService vcnManagementF = vcnManagement;
final WindowManagerService windowManagerF = wm;
final ConnectivityManager connectivityF = (ConnectivityManager)
@@ -2445,6 +2456,15 @@ public final class SystemServer {
reportWtf("making Connectivity Service ready", e);
}
t.traceEnd();
t.traceBegin("MakeVpnManagerServiceReady");
try {
if (vpnManagerF != null) {
vpnManagerF.systemReady();
}
} catch (Throwable e) {
reportWtf("making VpnManagerService ready", e);
}
t.traceEnd();
t.traceBegin("MakeVcnManagementServiceReady");
try {
if (vcnManagementF != null) {