(一百九十一)Android Q 学习setProcessDefaultNetwork
目录1.API2.实现2.1 getNetIdForResolv2.2bindProcessToNetwork2.2.1 client2.2.2 server3.network权限设置4. 总结1.APIpublic static boolean setProcessDefaultNetwork(@Nullable Network network)这...
目录
1.API
public static boolean setProcessDefaultNetwork(@Nullable Network network)
这是ConnectivityManager的接口,传入network参数,看下注释学习下这个api的功能。
/**
* Binds the current process to {@code network}. All Sockets created in the future
* (and not explicitly bound via a bound SocketFactory from
* {@link Network#getSocketFactory() Network.getSocketFactory()}) will be bound to
* {@code network}. All host name resolutions will be limited to {@code network} as well.
* Note that if {@code network} ever disconnects, all Sockets created in this way will cease to
* work and all host name resolutions will fail. This is by design so an application doesn't
* accidentally use Sockets it thinks are still bound to a particular {@link Network}.
* To clear binding pass {@code null} for {@code network}. Using individually bound
* Sockets created by Network.getSocketFactory().createSocket() and
* performing network-specific host name resolutions via
* {@link Network#getAllByName Network.getAllByName} is preferred to calling
* {@code setProcessDefaultNetwork}.
*
* @param network The {@link Network} to bind the current process to, or {@code null} to clear
* the current binding.
* @return {@code true} on success, {@code false} if the {@link Network} is no longer valid.
* @deprecated This function can throw {@link IllegalStateException}. Use
* {@link #bindProcessToNetwork} instead. {@code bindProcessToNetwork}
* is a direct replacement.
*/
简而言之就是将当前进程与network绑定起来,这样这个进程以后创建的socket都会绑定到这个network(除了显示绑定到SocketFactory的)
2.实现
public static boolean setProcessDefaultNetwork(@Nullable Network network) {
int netId = (network == null) ? NETID_UNSET : network.netId;
boolean isSameNetId = (netId == NetworkUtils.getBoundNetworkForProcess());
if (netId != NETID_UNSET) {
netId = network.getNetIdForResolv();
}
if (!NetworkUtils.bindProcessToNetwork(netId)) {
return false;
}
if (!isSameNetId) {
// Set HTTP proxy system properties to match network.
// TODO: Deprecate this static method and replace it with a non-static version.
try {
Proxy.setHttpProxySystemProperty(getInstance().getDefaultProxy());
} catch (SecurityException e) {
// The process doesn't have ACCESS_NETWORK_STATE, so we can't fetch the proxy.
Log.e(TAG, "Can't set proxy properties", e);
}
// Must flush DNS cache as new network may have different DNS resolutions.
InetAddress.clearDnsCache();
// Must flush socket pool as idle sockets will be bound to previous network and may
// cause subsequent fetches to be performed on old network.
NetworkEventDispatcher.getInstance().onNetworkConfigurationChanged();
}
return true;
}
简单来看就是
1)先获取当前进程bind的network id,如果不同才重新绑定;
2)获取解析的network id
3)bind完成后的一些后续工作,比如http代理,dns和清除socket池
2.1 getNetIdForResolv
/**
* Returns a netid marked with the Private DNS bypass flag.
*
* This flag must be kept in sync with the NETID_USE_LOCAL_NAMESERVERS flag
* in system/netd/include/NetdClient.h.
*
* @hide
*/
public int getNetIdForResolv() {
return mPrivateDnsBypass
? (int) (0x80000000L | (long) netId) // Non-portable DNS resolution flag.
: netId;
}
大概意思是flag要和netd的一致
2.2 bindProcessToNetwork
NetworkUtils
/**
* Binds the current process to the network designated by {@code netId}. All sockets created
* in the future (and not explicitly bound via a bound {@link SocketFactory} (see
* {@link Network#getSocketFactory}) will be bound to this network. Note that if this
* {@code Network} ever disconnects all sockets created in this way will cease to work. This
* is by design so an application doesn't accidentally use sockets it thinks are still bound to
* a particular {@code Network}. Passing NETID_UNSET clears the binding.
*/
public native static boolean bindProcessToNetwork(int netId);
这是一个NetworkUtils的静态native方法,通过jni继续往下调用
android_net_NetUtils.cpp
/*
* JNI registration.
*/
static const JNINativeMethod gNetworkUtilMethods[] = {
/* name, signature, funcPtr */
{ "bindProcessToNetwork", "(I)Z", (void*) android_net_utils_bindProcessToNetwork },
+
static jboolean android_net_utils_bindProcessToNetwork(JNIEnv *env, jobject thiz, jint netId)
{
return (jboolean) !setNetworkForProcess(netId);
}
2.2.1 client
http://androidxref.com/9.0.0_r3/xref/system/netd/client/NetdClient.cpp#219
extern "C" int setNetworkForProcess(unsigned netId) {
return setNetworkForTarget(netId, &netIdForProcess);
}
int setNetworkForTarget(unsigned netId, std::atomic_uint* target) {
const unsigned requestedNetId = netId;
netId &= ~NETID_USE_LOCAL_NAMESERVERS;
if (netId == NETID_UNSET) {
*target = netId;
return 0;
}
// Verify that we are allowed to use |netId|, by creating a socket and trying to have it marked
// with the netId. Call libcSocket() directly; else the socket creation (via netdClientSocket())
// might itself cause another check with the fwmark server, which would be wasteful.
const auto socketFunc = libcSocket ? libcSocket : socket;
int socketFd = socketFunc(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (socketFd < 0) {
return -errno;
}
int error = setNetworkForSocket(netId, socketFd);
if (!error) {
*target = requestedNetId;
}
close(socketFd);
return error;
}
创建一个ipv6的socket,然后调用
extern "C" int setNetworkForSocket(unsigned netId, int socketFd) {
CHECK_SOCKET_IS_MARKABLE(socketFd);
FwmarkCommand command = {FwmarkCommand::SELECT_NETWORK, netId, 0, 0};
return FwmarkClient().send(&command, socketFd, nullptr);
}
#define CHECK_SOCKET_IS_MARKABLE(sock) \
do { \
int err; \
if ((err = checkSocket(sock)) != 0) { \
return err; \
} \
} while (false);
int checkSocket(int socketFd) {
if (socketFd < 0) {
return -EBADF;
}
int family;
socklen_t familyLen = sizeof(family);
if (getsockopt(socketFd, SOL_SOCKET, SO_DOMAIN, &family, &familyLen) == -1) {
return -errno;
}
if (!FwmarkClient::shouldSetFwmark(family)) {
return -EAFNOSUPPORT;
}
return 0;
}
这边都是NetdClient的代码,大概意思是向netd server发送一个 FwmarkCommand::SELECT_NETWORK命令,选择的network id是netid
2.2.2 server
http://androidxref.com/9.0.0_r3/xref/system/netd/server/FwmarkServer.cpp#248
case FwmarkCommand::SELECT_NETWORK: {
fwmark.netId = command.netId;
if (command.netId == NETID_UNSET) {
fwmark.explicitlySelected = false;
fwmark.protectedFromVpn = false;
permission = PERMISSION_NONE;
} else {
if (int ret = mNetworkController->checkUserNetworkAccess(client->getUid(),
command.netId)) {
return ret;
}
fwmark.explicitlySelected = true;
fwmark.protectedFromVpn = mNetworkController->canProtect(client->getUid());
}
break;
}
这边看到设置之前会做一个权限校验,主要参数是apk的uid和network的netid
int NetworkController::checkUserNetworkAccess(uid_t uid, unsigned netId) const {
ScopedRLock lock(mRWLock);
return checkUserNetworkAccessLocked(uid, netId);
}
int NetworkController::checkUserNetworkAccessLocked(uid_t uid, unsigned netId) const {
Network* network = getNetworkLocked(netId);
if (!network) {
return -ENONET;
}
// If uid is INVALID_UID, this likely means that we were unable to retrieve the UID of the peer
// (using SO_PEERCRED). Be safe and deny access to the network, even if it's valid.
if (uid == INVALID_UID) {
return -EREMOTEIO;
}
Permission userPermission = getPermissionForUserLocked(uid);
if ((userPermission & PERMISSION_SYSTEM) == PERMISSION_SYSTEM) {
return 0;
}
if (network->getType() == Network::VIRTUAL) {
return static_cast<VirtualNetwork*>(network)->appliesToUser(uid) ? 0 : -EPERM;
}
VirtualNetwork* virtualNetwork = getVirtualNetworkForUserLocked(uid);
if (virtualNetwork && virtualNetwork->isSecure() &&
mProtectableUsers.find(uid) == mProtectableUsers.end()) {
return -EPERM;
}
Permission networkPermission = static_cast<PhysicalNetwork*>(network)->getPermission();
return ((userPermission & networkPermission) == networkPermission) ? 0 : -EACCES;
}
权限校验是先看uid是否有系统权限,有的话return,VIRTUAL的先不看,然后获取到当前网络需要的权限,如果当前uid的权限大于当前网络需要的权限,则校验通过,否则失败。
网络权限可以通过dumpsys netd看到
看下权限列表
http://androidxref.com/9.0.0_r3/xref/system/netd/include/Permission.h
// This enum represents the permissions we care about for networking. When applied to an app, it's
// the permission the app (UID) has been granted. When applied to a network, it's the permission an
// app must hold to be allowed to use the network. PERMISSION_NONE means "no special permission is
// held by the app" or "no special permission is required to use the network".
//
// Permissions are flags that can be OR'ed together to represent combinations of permissions.
//
// PERMISSION_NONE is used for regular networks and apps, such as those that hold the
// android.permission.INTERNET framework permission.
//
// PERMISSION_NETWORK is used for privileged networks and apps that can manipulate or access them,
// such as those that hold the android.permission.CHANGE_NETWORK_STATE framework permission.
//
// PERMISSION_SYSTEM is used for system apps, such as those that are installed on the system
// partition, those that hold the android.permission.CONNECTIVITY_INTERNAL framework permission and
// those whose UID is less than FIRST_APPLICATION_UID.
enum Permission {
PERMISSION_NONE = 0x0,
PERMISSION_NETWORK = 0x1,
PERMISSION_SYSTEM = 0x3, // Includes PERMISSION_NETWORK.
};
inline const char *permissionToName(Permission permission) {
switch (permission) {
case PERMISSION_NONE: return "NONE";
case PERMISSION_NETWORK: return "NETWORK";
case PERMISSION_SYSTEM: return "SYSTEM";
// No default statement. We want to see errors of the form:
// "enumeration value 'PERMISSION_SYSTEM' not handled in switch [-Werror,-Wswitch]".
}
}
这边有权限声明和Android 权限的对应关系
3.network权限设置
1)创建网络的时候会附带设置权限
int NetworkController::createPhysicalNetworkLocked(unsigned netId, Permission permission) {
if (!((MIN_NET_ID <= netId && netId <= MAX_NET_ID) ||
(MIN_OEM_ID <= netId && netId <= MAX_OEM_ID))) {
ALOGE("invalid netId %u", netId);
return -EINVAL;
}
if (isValidNetworkLocked(netId)) {
ALOGE("duplicate netId %u", netId);
return -EEXIST;
}
PhysicalNetwork* physicalNetwork = new PhysicalNetwork(netId, mDelegateImpl);
if (int ret = physicalNetwork->setPermission(permission)) {
ALOGE("inconceivable! setPermission cannot fail on an empty network");
delete physicalNetwork;
return ret;
}
mNetworks[netId] = physicalNetwork;
updateTcpSocketMonitorPolling();
return 0;
}
2)有单独api可以批量设置
int NetworkController::setPermissionForNetworks(Permission permission,
const std::vector<unsigned>& netIds) {
ScopedWLock lock(mRWLock);
for (unsigned netId : netIds) {
Network* network = getNetworkLocked(netId);
if (!network) {
ALOGE("no such netId %u", netId);
return -ENONET;
}
if (network->getType() != Network::PHYSICAL) {
ALOGE("cannot set permissions on non-physical network with netId %u", netId);
return -EINVAL;
}
if (int ret = static_cast<PhysicalNetwork*>(network)->setPermission(permission)) {
return ret;
}
}
return 0;
}
看下这两个api对应到上层是什么
./server/NetdNativeService.cpp
binder::Status NetdNativeService::networkSetPermissionForNetwork(int32_t netId,
int32_t permission) {
ENFORCE_NETWORK_STACK_PERMISSIONS();
std::vector<unsigned> netIds = {(unsigned) netId};
int res = gCtls->netCtrl.setPermissionForNetworks(convertPermission(permission), netIds);
return statusFromErrcode(res);
}
./server/NdcDispatcher.cpp
// 0 1 2 3 4 5
// network permission user set <permission> <uid> ...
// network permission user clear <uid> ...
// network permission network set <permission> <netId> ...
// network permission network clear <netId> ...
if (!strcmp(argv[1], "permission")) {
if (argc < 5) {
return syntaxError(cli, "Missing argument");
}
int nextArg = 4;
int permission = INetd::PERMISSION_NONE;
if (!strcmp(argv[3], "set")) {
permission = stringToINetdPermission(argv[4]);
if (permission == INetd::PERMISSION_NONE) {
return syntaxError(cli, "Unknown permission");
}
nextArg = 5;
} else if (strcmp(argv[3], "clear")) {
return syntaxError(cli, "Unknown argument");
}
if (nextArg == argc) {
return syntaxError(cli, "Missing id");
}
bool userPermissions = !strcmp(argv[2], "user");
bool networkPermissions = !strcmp(argv[2], "network");
if (!userPermissions && !networkPermissions) {
return syntaxError(cli, "Unknown argument");
}
std::vector<int32_t> ids;
for (; nextArg < argc; ++nextArg) {
if (userPermissions) {
char* endPtr;
unsigned id = strtoul(argv[nextArg], &endPtr, 0);
if (!*argv[nextArg] || *endPtr) {
return syntaxError(cli, "Invalid id");
}
ids.push_back(id);
} else {
// networkPermissions
ids.push_back(stringToNetId(argv[nextArg]));
}
}
if (userPermissions) {
mNetd->networkSetPermissionForUser(permission, ids);
} else {
// networkPermissions
for (auto netId : ids) {
Status status = mNetd->networkSetPermissionForNetwork(netId, permission);
if (!status.isOk())
return operationError(cli, "setPermissionForNetworks() failed",
status.serviceSpecificErrorCode());
}
}
return success(cli);
}
通过ndc命令设过来的
NetworkManagementService
@Override
public void setNetworkPermission(int netId, int permission) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
mNetdService.networkSetPermissionForNetwork(netId, permission);
} catch (RemoteException | ServiceSpecificException e) {
throw new IllegalStateException(e);
}
}
ConnectivityService
private void updateCapabilities(int oldScore, NetworkAgentInfo nai, NetworkCapabilities nc) {
NetworkCapabilities newNc = mixInCapabilities(nai, nc);
if (Objects.equals(nai.networkCapabilities, newNc)) return;
final int oldPermission = getNetworkPermission(nai.networkCapabilities);
final int newPermission = getNetworkPermission(newNc);
if (oldPermission != newPermission && nai.created && !nai.isVPN()) {
try {
mNMS.setNetworkPermission(nai.network.netId, newPermission);
} catch (RemoteException e) {
loge("Exception in setNetworkPermission: " + e);
}
}
网络主要根据它的NetworkCapabilities设置权限
private int getNetworkPermission(NetworkCapabilities nc) {
if (!nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)) {
return INetd.PERMISSION_SYSTEM;
}
if (!nc.hasCapability(NET_CAPABILITY_FOREGROUND)) {
return INetd.PERMISSION_NETWORK;
}
return INetd.PERMISSION_NONE;
}
其中
if (nai.isBackgroundNetwork()) {
newNc.removeCapability(NET_CAPABILITY_FOREGROUND);
} else {
newNc.addCapability(NET_CAPABILITY_FOREGROUND);
}
如果一个网络有前台request,并且后台request大于0,那么它就有NET_CAPABILITY_FOREGROUND,则它就不要网络权限,否则则需要网络权限才可使用这个网络。
NetworkAgentInfo
/**
* Returns whether the network is a background network. A network is a background network if it
* does not have the NET_CAPABILITY_FOREGROUND capability, which implies it is satisfying no
* foreground request, is not lingering (i.e. kept for a while after being outscored), and is
* not a speculative network (i.e. kept pending validation when validation would have it
* outscore another foreground network). That implies it is being kept up by some background
* request (otherwise it would be torn down), maybe the mobile always-on request.
*/
public boolean isBackgroundNetwork() {
return !isVPN() && numForegroundNetworkRequests() == 0 && mNumBackgroundNetworkRequests > 0
&& !isLingering();
}
4. 总结
更多推荐
所有评论(0)