blob: 9d21b9241c0d49abfb303e4d302de9875eb32a88 [file] [log] [blame]
/*
* 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 com.android.server.vcn;
import android.annotation.NonNull;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.vcn.VcnConfig;
import android.net.vcn.VcnGatewayConnectionConfig;
import android.os.Handler;
import android.os.Message;
import android.os.ParcelUuid;
import android.util.Slog;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
* Represents an single instance of a VCN.
*
* <p>Each Vcn instance manages all {@link VcnGatewayConnection}(s) for a given subscription group,
* including per-capability networks, network selection, and multi-homing.
*
* @hide
*/
public class Vcn extends Handler {
private static final String TAG = Vcn.class.getSimpleName();
private static final int MSG_EVENT_BASE = 0;
private static final int MSG_CMD_BASE = 100;
/**
* A carrier app updated the configuration.
*
* <p>Triggers update of config, re-evaluating all active and underlying networks.
*
* @param obj VcnConfig
*/
private static final int MSG_EVENT_CONFIG_UPDATED = MSG_EVENT_BASE;
/**
* A NetworkRequest was added or updated.
*
* <p>Triggers an evaluation of all active networks, bringing up a new one if necessary.
*
* @param obj NetworkRequest
*/
private static final int MSG_EVENT_NETWORK_REQUESTED = MSG_EVENT_BASE + 1;
/** Triggers an immediate teardown of the entire Vcn, including GatewayConnections. */
private static final int MSG_CMD_TEARDOWN = MSG_CMD_BASE;
@NonNull private final VcnContext mVcnContext;
@NonNull private final ParcelUuid mSubscriptionGroup;
@NonNull private final Dependencies mDeps;
@NonNull private final VcnNetworkRequestListener mRequestListener;
@NonNull
private final Map<VcnGatewayConnectionConfig, VcnGatewayConnection> mVcnGatewayConnections =
new HashMap<>();
@NonNull private VcnConfig mConfig;
private boolean mIsRunning = true;
public Vcn(
@NonNull VcnContext vcnContext,
@NonNull ParcelUuid subscriptionGroup,
@NonNull VcnConfig config) {
this(vcnContext, subscriptionGroup, config, new Dependencies());
}
private Vcn(
@NonNull VcnContext vcnContext,
@NonNull ParcelUuid subscriptionGroup,
@NonNull VcnConfig config,
@NonNull Dependencies deps) {
super(Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper());
mVcnContext = vcnContext;
mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup");
mDeps = Objects.requireNonNull(deps, "Missing deps");
mRequestListener = new VcnNetworkRequestListener();
mConfig = Objects.requireNonNull(config, "Missing config");
// Register to receive cached and future NetworkRequests
mVcnContext.getVcnNetworkProvider().registerListener(mRequestListener);
}
/** Asynchronously updates the configuration and triggers a re-evaluation of Networks */
public void updateConfig(@NonNull VcnConfig config) {
Objects.requireNonNull(config, "Missing config");
sendMessage(obtainMessage(MSG_EVENT_CONFIG_UPDATED, config));
}
/** Asynchronously tears down this Vcn instance, including VcnGatewayConnection(s) */
public void teardownAsynchronously() {
sendMessageAtFrontOfQueue(obtainMessage(MSG_CMD_TEARDOWN));
}
private class VcnNetworkRequestListener implements VcnNetworkProvider.NetworkRequestListener {
@Override
public void onNetworkRequested(@NonNull NetworkRequest request, int score, int providerId) {
Objects.requireNonNull(request, "Missing request");
sendMessage(obtainMessage(MSG_EVENT_NETWORK_REQUESTED, score, providerId, request));
}
}
@Override
public void handleMessage(@NonNull Message msg) {
if (!mIsRunning) {
return;
}
switch (msg.what) {
case MSG_EVENT_CONFIG_UPDATED:
handleConfigUpdated((VcnConfig) msg.obj);
break;
case MSG_EVENT_NETWORK_REQUESTED:
handleNetworkRequested((NetworkRequest) msg.obj, msg.arg1, msg.arg2);
break;
case MSG_CMD_TEARDOWN:
handleTeardown();
break;
default:
Slog.wtf(getLogTag(), "Unknown msg.what: " + msg.what);
}
}
private void handleConfigUpdated(@NonNull VcnConfig config) {
// TODO: Add a dump function in VcnConfig that omits PII. Until then, use hashCode()
Slog.v(getLogTag(), String.format("Config updated: config = %s", config.hashCode()));
mConfig = config;
// TODO: Reevaluate active VcnGatewayConnection(s)
}
private void handleTeardown() {
mVcnContext.getVcnNetworkProvider().unregisterListener(mRequestListener);
for (VcnGatewayConnection gatewayConnection : mVcnGatewayConnections.values()) {
gatewayConnection.teardownAsynchronously();
}
mIsRunning = false;
}
private void handleNetworkRequested(
@NonNull NetworkRequest request, int score, int providerId) {
if (score > getNetworkScore()) {
Slog.v(getLogTag(),
"Request " + request.requestId + " already satisfied by higher-scoring ("
+ score + ") network from provider " + providerId);
return;
}
// If preexisting VcnGatewayConnection(s) satisfy request, return
for (VcnGatewayConnectionConfig gatewayConnectionConfig : mVcnGatewayConnections.keySet()) {
if (requestSatisfiedByGatewayConnectionConfig(request, gatewayConnectionConfig)) {
Slog.v(getLogTag(),
"Request " + request.requestId
+ " satisfied by existing VcnGatewayConnection");
return;
}
}
// If any supported (but not running) VcnGatewayConnection(s) can satisfy request, bring it
// up
for (VcnGatewayConnectionConfig gatewayConnectionConfig :
mConfig.getGatewayConnectionConfigs()) {
if (requestSatisfiedByGatewayConnectionConfig(request, gatewayConnectionConfig)) {
Slog.v(
getLogTag(),
"Bringing up new VcnGatewayConnection for request " + request.requestId);
final VcnGatewayConnection vcnGatewayConnection =
new VcnGatewayConnection(
mVcnContext, mSubscriptionGroup, gatewayConnectionConfig);
mVcnGatewayConnections.put(gatewayConnectionConfig, vcnGatewayConnection);
}
}
}
private boolean requestSatisfiedByGatewayConnectionConfig(
@NonNull NetworkRequest request, @NonNull VcnGatewayConnectionConfig config) {
final NetworkCapabilities configCaps = new NetworkCapabilities();
for (int cap : config.getAllExposedCapabilities()) {
configCaps.addCapability(cap);
}
return request.networkCapabilities.satisfiedByNetworkCapabilities(configCaps);
}
private String getLogTag() {
return String.format("%s [%d]", TAG, mSubscriptionGroup.hashCode());
}
/** Retrieves the network score for a VCN Network */
private int getNetworkScore() {
// TODO: STOPSHIP (b/173549607): Make this use new NetworkSelection, or some magic "max in
// subGrp" value
return 52;
}
private static class Dependencies {}
}