All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src.android.net.vcn.VcnManager Maven / Gradle / Ivy

Go to download

A library jar that provides APIs for Applications written for the Google Android Platform.

There is a newer version: 15-robolectric-12650502
Show newest version
/*
 * 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.vcn;

import static java.util.Objects.requireNonNull;

import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.content.Context;
import android.net.LinkProperties;
import android.net.NetworkCapabilities;
import android.os.Binder;
import android.os.ParcelUuid;
import android.os.RemoteException;
import android.os.ServiceSpecificException;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;

import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;

/**
 * VcnManager publishes APIs for applications to configure and manage Virtual Carrier Networks.
 *
 * 

A VCN creates a virtualization layer to allow carriers to aggregate heterogeneous physical * networks, unifying them as a single carrier network. This enables infrastructure flexibility on * the part of carriers without impacting user connectivity, abstracting the physical network * technologies as an implementation detail of their public network. * *

Each VCN virtualizes a carrier's network by building tunnels to a carrier's core network over * carrier-managed physical links and supports a IP mobility layer to ensure seamless transitions * between the underlying networks. Each VCN is configured based on a Subscription Group (see {@link * android.telephony.SubscriptionManager}) and aggregates all networks that are brought up based on * a profile or suggestion in the specified Subscription Group. * *

The VCN can be configured to expose one or more {@link android.net.Network}(s), each with * different capabilities, allowing for APN virtualization. * *

If a tunnel fails to connect, or otherwise encounters a fatal error, the VCN will attempt to * reestablish the connection. If the tunnel still has not reconnected after a system-determined * timeout, the VCN Safe Mode (see below) will be entered. * *

The VCN Safe Mode ensures that users (and carriers) have a fallback to restore system * connectivity to update profiles, diagnose issues, contact support, or perform other remediation * tasks. In Safe Mode, the system will allow underlying cellular networks to be used as default. * Additionally, during Safe Mode, the VCN will continue to retry the connections, and will * automatically exit Safe Mode if all active tunnels connect successfully. */ @SystemService(Context.VCN_MANAGEMENT_SERVICE) public class VcnManager { @NonNull private static final String TAG = VcnManager.class.getSimpleName(); /** * Key for WiFi entry RSSI thresholds * *

The VCN will only migrate to a Carrier WiFi network that has a signal strength greater * than, or equal to this threshold. * *

WARNING: The VCN does not listen for changes to this key made after VCN startup. * * @hide */ @NonNull public static final String VCN_NETWORK_SELECTION_WIFI_ENTRY_RSSI_THRESHOLD_KEY = "vcn_network_selection_wifi_entry_rssi_threshold"; /** * Key for WiFi entry RSSI thresholds * *

If the VCN's selected Carrier WiFi network has a signal strength less than this threshold, * the VCN will attempt to migrate away from the Carrier WiFi network. * *

WARNING: The VCN does not listen for changes to this key made after VCN startup. * * @hide */ @NonNull public static final String VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY = "vcn_network_selection_wifi_exit_rssi_threshold"; // TODO: Add separate signal strength thresholds for 2.4 GHz and 5GHz private static final Map< VcnNetworkPolicyChangeListener, VcnUnderlyingNetworkPolicyListenerBinder> REGISTERED_POLICY_LISTENERS = new ConcurrentHashMap<>(); @NonNull private final Context mContext; @NonNull private final IVcnManagementService mService; /** * Construct an instance of VcnManager within an application context. * * @param ctx the application context for this manager * @param service the VcnManagementService binder backing this manager * * @hide */ public VcnManager(@NonNull Context ctx, @NonNull IVcnManagementService service) { mContext = requireNonNull(ctx, "missing context"); mService = requireNonNull(service, "missing service"); } /** * Get all currently registered VcnNetworkPolicyChangeListeners for testing purposes. * * @hide */ @VisibleForTesting(visibility = Visibility.PRIVATE) @NonNull public static Map getAllPolicyListeners() { return Collections.unmodifiableMap(REGISTERED_POLICY_LISTENERS); } /** * Sets the VCN configuration for a given subscription group. * *

An app that has carrier privileges for any of the subscriptions in the given group may set * a VCN configuration. If a configuration already exists for the given subscription group, it * will be overridden. Any active VCN(s) may be forced to restart to use the new configuration. * *

This API is ONLY permitted for callers running as the primary user. * * @param subscriptionGroup the subscription group that the configuration should be applied to * @param config the configuration parameters for the VCN * @throws SecurityException if the caller does not have carrier privileges for the provided * subscriptionGroup, or is not running as the primary user * @throws IOException if the configuration failed to be saved and persisted to disk. This may * occur due to temporary disk errors, or more permanent conditions such as a full disk. */ @RequiresPermission("carrier privileges") // TODO (b/72967236): Define a system-wide constant public void setVcnConfig(@NonNull ParcelUuid subscriptionGroup, @NonNull VcnConfig config) throws IOException { requireNonNull(subscriptionGroup, "subscriptionGroup was null"); requireNonNull(config, "config was null"); try { mService.setVcnConfig(subscriptionGroup, config, mContext.getOpPackageName()); } catch (ServiceSpecificException e) { throw new IOException(e); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Clears the VCN configuration for a given subscription group. * *

An app that has carrier privileges for any of the subscriptions in the given group may * clear a VCN configuration. This API is ONLY permitted for callers running as the primary * user. Any active VCN will be torn down. * * @param subscriptionGroup the subscription group that the configuration should be applied to * @throws SecurityException if the caller does not have carrier privileges, or is not running * as the primary user * @throws IOException if the configuration failed to be cleared from disk. This may occur due * to temporary disk errors, or more permanent conditions such as a full disk. */ @RequiresPermission("carrier privileges") // TODO (b/72967236): Define a system-wide constant public void clearVcnConfig(@NonNull ParcelUuid subscriptionGroup) throws IOException { requireNonNull(subscriptionGroup, "subscriptionGroup was null"); try { mService.clearVcnConfig(subscriptionGroup, mContext.getOpPackageName()); } catch (ServiceSpecificException e) { throw new IOException(e); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Retrieves the list of Subscription Groups for which a VCN Configuration has been set. * *

The returned list will include only subscription groups for which the carrier app is * privileged, and which have an associated {@link VcnConfig}. * * @throws SecurityException if the caller is not running as the primary user */ @NonNull public List getConfiguredSubscriptionGroups() { try { return mService.getConfiguredSubscriptionGroups(mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } // TODO(b/180537630): remove all VcnUnderlyingNetworkPolicyListener refs once Telephony is using // the new VcnNetworkPolicyChangeListener API /** * VcnUnderlyingNetworkPolicyListener is the interface through which internal system components * can register to receive updates for VCN-underlying Network policies from the System Server. * * @hide */ public interface VcnUnderlyingNetworkPolicyListener extends VcnNetworkPolicyChangeListener {} /** * Add a listener for VCN-underlying network policy updates. * * @param executor the Executor that will be used for invoking all calls to the specified * Listener * @param listener the VcnUnderlyingNetworkPolicyListener to be added * @throws SecurityException if the caller does not have permission NETWORK_FACTORY * @throws IllegalStateException if the specified VcnUnderlyingNetworkPolicyListener is already * registered * @hide */ @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void addVcnUnderlyingNetworkPolicyListener( @NonNull Executor executor, @NonNull VcnUnderlyingNetworkPolicyListener listener) { addVcnNetworkPolicyChangeListener(executor, listener); } /** * Remove the specified VcnUnderlyingNetworkPolicyListener from VcnManager. * *

If the specified listener is not currently registered, this is a no-op. * * @param listener the VcnUnderlyingNetworkPolicyListener that will be removed * @hide */ public void removeVcnUnderlyingNetworkPolicyListener( @NonNull VcnUnderlyingNetworkPolicyListener listener) { removeVcnNetworkPolicyChangeListener(listener); } /** * Queries the underlying network policy for a network with the given parameters. * *

Prior to a new NetworkAgent being registered, or upon notification that Carrier VCN policy * may have changed via {@link VcnUnderlyingNetworkPolicyListener#onPolicyChanged()}, a Network * Provider MUST poll for the updated Network policy based on that Network's capabilities and * properties. * * @param networkCapabilities the NetworkCapabilities to be used in determining the Network * policy for this Network. * @param linkProperties the LinkProperties to be used in determining the Network policy for * this Network. * @throws SecurityException if the caller does not have permission NETWORK_FACTORY * @return the VcnUnderlyingNetworkPolicy to be used for this Network. * @hide */ @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public VcnUnderlyingNetworkPolicy getUnderlyingNetworkPolicy( @NonNull NetworkCapabilities networkCapabilities, @NonNull LinkProperties linkProperties) { requireNonNull(networkCapabilities, "networkCapabilities must not be null"); requireNonNull(linkProperties, "linkProperties must not be null"); try { return mService.getUnderlyingNetworkPolicy(networkCapabilities, linkProperties); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * VcnNetworkPolicyChangeListener is the interface through which internal system components * (e.g. Network Factories) can register to receive updates for VCN-underlying Network policies * from the System Server. * *

Any Network Factory that brings up Networks capable of being VCN-underlying Networks * should register a VcnNetworkPolicyChangeListener. VcnManager will then use this listener to * notify the registrant when VCN Network policies change. Upon receiving this signal, the * listener must check {@link VcnManager} for the current Network policy result for each of its * Networks via {@link #applyVcnNetworkPolicy(NetworkCapabilities, LinkProperties)}. * * @hide */ @SystemApi public interface VcnNetworkPolicyChangeListener { /** * Notifies the implementation that the VCN's underlying Network policy has changed. * *

After receiving this callback, implementations should get the current {@link * VcnNetworkPolicyResult} via {@link #applyVcnNetworkPolicy(NetworkCapabilities, * LinkProperties)}. */ void onPolicyChanged(); } /** * Add a listener for VCN-underlying Network policy updates. * *

A {@link VcnNetworkPolicyChangeListener} is eligible to begin receiving callbacks once it * is registered. No callbacks are guaranteed upon registration. * * @param executor the Executor that will be used for invoking all calls to the specified * Listener * @param listener the VcnNetworkPolicyChangeListener to be added * @throws SecurityException if the caller does not have permission NETWORK_FACTORY * @throws IllegalStateException if the specified VcnNetworkPolicyChangeListener is already * registered * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void addVcnNetworkPolicyChangeListener( @NonNull Executor executor, @NonNull VcnNetworkPolicyChangeListener listener) { requireNonNull(executor, "executor must not be null"); requireNonNull(listener, "listener must not be null"); VcnUnderlyingNetworkPolicyListenerBinder binder = new VcnUnderlyingNetworkPolicyListenerBinder(executor, listener); if (REGISTERED_POLICY_LISTENERS.putIfAbsent(listener, binder) != null) { throw new IllegalStateException("listener is already registered with VcnManager"); } try { mService.addVcnUnderlyingNetworkPolicyListener(binder); } catch (RemoteException e) { REGISTERED_POLICY_LISTENERS.remove(listener); throw e.rethrowFromSystemServer(); } } /** * Remove the specified VcnNetworkPolicyChangeListener from VcnManager. * *

If the specified listener is not currently registered, this is a no-op. * * @param listener the VcnNetworkPolicyChangeListener that will be removed * @throws SecurityException if the caller does not have permission NETWORK_FACTORY * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void removeVcnNetworkPolicyChangeListener( @NonNull VcnNetworkPolicyChangeListener listener) { requireNonNull(listener, "listener must not be null"); VcnUnderlyingNetworkPolicyListenerBinder binder = REGISTERED_POLICY_LISTENERS.remove(listener); if (binder == null) { return; } try { mService.removeVcnUnderlyingNetworkPolicyListener(binder); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Applies the network policy for a {@link android.net.Network} with the given parameters. * *

Prior to a new NetworkAgent being registered, or upon notification that Carrier VCN policy * may have changed via {@link VcnNetworkPolicyChangeListener#onPolicyChanged()}, a Network * Provider MUST poll for the updated Network policy based on that Network's capabilities and * properties. * * @param networkCapabilities the NetworkCapabilities to be used in determining the Network * policy result for this Network. * @param linkProperties the LinkProperties to be used in determining the Network policy result * for this Network. * @throws SecurityException if the caller does not have permission NETWORK_FACTORY * @return the {@link VcnNetworkPolicyResult} to be used for this Network. * @hide */ @NonNull @SystemApi @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public VcnNetworkPolicyResult applyVcnNetworkPolicy( @NonNull NetworkCapabilities networkCapabilities, @NonNull LinkProperties linkProperties) { requireNonNull(networkCapabilities, "networkCapabilities must not be null"); requireNonNull(linkProperties, "linkProperties must not be null"); final VcnUnderlyingNetworkPolicy policy = getUnderlyingNetworkPolicy(networkCapabilities, linkProperties); return new VcnNetworkPolicyResult( policy.isTeardownRequested(), policy.getMergedNetworkCapabilities()); } /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef({ VCN_STATUS_CODE_NOT_CONFIGURED, VCN_STATUS_CODE_INACTIVE, VCN_STATUS_CODE_ACTIVE, VCN_STATUS_CODE_SAFE_MODE }) public @interface VcnStatusCode {} /** * Value indicating that the VCN for the subscription group is not configured, or that the * callback is not privileged for the subscription group. */ public static final int VCN_STATUS_CODE_NOT_CONFIGURED = 0; /** * Value indicating that the VCN for the subscription group is inactive. * *

A VCN is inactive if a {@link VcnConfig} is present for the subscription group, but the * provisioning package is not privileged. */ public static final int VCN_STATUS_CODE_INACTIVE = 1; /** * Value indicating that the VCN for the subscription group is active. * *

A VCN is active if a {@link VcnConfig} is present for the subscription, the provisioning * package is privileged, and the VCN is not in Safe Mode. In other words, a VCN is considered * active while it is connecting, fully connected, and disconnecting. */ public static final int VCN_STATUS_CODE_ACTIVE = 2; /** * Value indicating that the VCN for the subscription group is in Safe Mode. * *

A VCN will be put into Safe Mode if any of the gateway connections were unable to * establish a connection within a system-determined timeout (while underlying networks were * available). */ public static final int VCN_STATUS_CODE_SAFE_MODE = 3; /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef({ VCN_ERROR_CODE_INTERNAL_ERROR, VCN_ERROR_CODE_CONFIG_ERROR, VCN_ERROR_CODE_NETWORK_ERROR }) public @interface VcnErrorCode {} /** * Value indicating that an internal failure occurred in this Gateway Connection. */ public static final int VCN_ERROR_CODE_INTERNAL_ERROR = 0; /** * Value indicating that an error with this Gateway Connection's configuration occurred. * *

For example, this error code will be returned after authentication failures. */ public static final int VCN_ERROR_CODE_CONFIG_ERROR = 1; /** * Value indicating that a Network error occurred with this Gateway Connection. * *

For example, this error code will be returned if an underlying {@link android.net.Network} * for this Gateway Connection is lost, or if an error occurs while resolving the connection * endpoint address. */ public static final int VCN_ERROR_CODE_NETWORK_ERROR = 2; /** * VcnStatusCallback is the interface for Carrier apps to receive updates for their VCNs. * *

VcnStatusCallbacks may be registered before {@link VcnConfig}s are provided for a * subscription group. */ public abstract static class VcnStatusCallback { private VcnStatusCallbackBinder mCbBinder; /** * Invoked when status of the VCN for this callback's subscription group changes. * * @param statusCode the code for the status change encountered by this {@link * VcnStatusCallback}'s subscription group. This value will be one of VCN_STATUS_CODE_*. */ public abstract void onStatusChanged(@VcnStatusCode int statusCode); /** * Invoked when a VCN Gateway Connection corresponding to this callback's subscription group * encounters an error. * * @param gatewayConnectionName the String GatewayConnection name for the GatewayConnection * encountering an error. This will match the name for exactly one {@link * VcnGatewayConnectionConfig} for the {@link VcnConfig} configured for this callback's * subscription group * @param errorCode the code to indicate the error that occurred. This value will be one of * VCN_ERROR_CODE_*. * @param detail Throwable to provide additional information about the error, or {@code * null} if none */ public abstract void onGatewayConnectionError( @NonNull String gatewayConnectionName, @VcnErrorCode int errorCode, @Nullable Throwable detail); } /** * Registers the given callback to receive status updates for the specified subscription. * *

Callbacks can be registered for a subscription before {@link VcnConfig}s are set for it. * *

A {@link VcnStatusCallback} may only be registered for one subscription at a time. {@link * VcnStatusCallback}s may be reused once unregistered. * *

A {@link VcnStatusCallback} will only be invoked if the registering package has carrier * privileges for the specified subscription at the time of invocation. * *

A {@link VcnStatusCallback} is eligible to begin receiving callbacks once it is registered * and there is a VCN active for its specified subscription group (this may happen after the * callback is registered). * *

{@link VcnStatusCallback#onStatusChanged(int)} will be invoked on registration with the * current status for the specified subscription group's VCN. If the registrant is not * privileged for this subscription group, {@link #VCN_STATUS_CODE_NOT_CONFIGURED} will be * returned. * * @param subscriptionGroup The subscription group to match for callbacks * @param executor The {@link Executor} to be used for invoking callbacks * @param callback The VcnStatusCallback to be registered * @throws IllegalStateException if callback is currently registered with VcnManager */ public void registerVcnStatusCallback( @NonNull ParcelUuid subscriptionGroup, @NonNull Executor executor, @NonNull VcnStatusCallback callback) { requireNonNull(subscriptionGroup, "subscriptionGroup must not be null"); requireNonNull(executor, "executor must not be null"); requireNonNull(callback, "callback must not be null"); synchronized (callback) { if (callback.mCbBinder != null) { throw new IllegalStateException("callback is already registered with VcnManager"); } callback.mCbBinder = new VcnStatusCallbackBinder(executor, callback); try { mService.registerVcnStatusCallback( subscriptionGroup, callback.mCbBinder, mContext.getOpPackageName()); } catch (RemoteException e) { callback.mCbBinder = null; throw e.rethrowFromSystemServer(); } } } /** * Unregisters the given callback. * *

Once unregistered, the callback will stop receiving status updates for the subscription it * was registered with. * * @param callback The callback to be unregistered */ public void unregisterVcnStatusCallback(@NonNull VcnStatusCallback callback) { requireNonNull(callback, "callback must not be null"); synchronized (callback) { if (callback.mCbBinder == null) { // no Binder attached to this callback, so it's not currently registered return; } try { mService.unregisterVcnStatusCallback(callback.mCbBinder); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } finally { callback.mCbBinder = null; } } } /** * Binder wrapper for added VcnNetworkPolicyChangeListeners to receive signals from System * Server. * * @hide */ private static class VcnUnderlyingNetworkPolicyListenerBinder extends IVcnUnderlyingNetworkPolicyListener.Stub { @NonNull private final Executor mExecutor; @NonNull private final VcnNetworkPolicyChangeListener mListener; private VcnUnderlyingNetworkPolicyListenerBinder( Executor executor, VcnNetworkPolicyChangeListener listener) { mExecutor = executor; mListener = listener; } @Override public void onPolicyChanged() { Binder.withCleanCallingIdentity( () -> mExecutor.execute(() -> mListener.onPolicyChanged())); } } /** * Binder wrapper for VcnStatusCallbacks to receive signals from VcnManagementService. * * @hide */ @VisibleForTesting(visibility = Visibility.PRIVATE) public static class VcnStatusCallbackBinder extends IVcnStatusCallback.Stub { @NonNull private final Executor mExecutor; @NonNull private final VcnStatusCallback mCallback; public VcnStatusCallbackBinder( @NonNull Executor executor, @NonNull VcnStatusCallback callback) { mExecutor = executor; mCallback = callback; } @Override public void onVcnStatusChanged(@VcnStatusCode int statusCode) { Binder.withCleanCallingIdentity( () -> mExecutor.execute(() -> mCallback.onStatusChanged(statusCode))); } // TODO(b/180521637): use ServiceSpecificException for safer Exception 'parceling' @Override public void onGatewayConnectionError( @NonNull String gatewayConnectionName, @VcnErrorCode int errorCode, @Nullable String exceptionClass, @Nullable String exceptionMessage) { final Throwable cause = createThrowableByClassName(exceptionClass, exceptionMessage); Binder.withCleanCallingIdentity( () -> mExecutor.execute( () -> mCallback.onGatewayConnectionError( gatewayConnectionName, errorCode, cause))); } private static Throwable createThrowableByClassName( @Nullable String className, @Nullable String message) { if (className == null) { return null; } try { Class c = Class.forName(className); return (Throwable) c.getConstructor(String.class).newInstance(message); } catch (ReflectiveOperationException | ClassCastException e) { return new RuntimeException(className + ": " + message); } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy