/* * Copyright 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.internal.telephony.data; import android.annotation.ElapsedRealtimeLong; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.net.NetworkCapabilities; import android.net.NetworkRequest; import android.net.NetworkSpecifier; import android.os.SystemClock; import android.telephony.Annotation.NetCapability; import android.telephony.data.ApnSetting; import android.telephony.data.DataProfile; import android.telephony.data.TrafficDescriptor; import android.telephony.data.TrafficDescriptor.OsAppId; import com.android.internal.telephony.Phone; import com.android.internal.telephony.flags.FeatureFlags; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.AbstractMap.SimpleImmutableEntry; import java.util.Arrays; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.stream.Collectors; /** * TelephonyNetworkRequest is a wrapper class on top of {@link NetworkRequest}, which is originated * from the apps to request network. This class is intended to track supplemental information * related to this request, for example priority, evaluation result, whether this request is * actively being satisfied, timestamp, etc... * */ public class TelephonyNetworkRequest { @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = {"REQUEST_STATE_"}, value = { REQUEST_STATE_UNSATISFIED, REQUEST_STATE_SATISFIED}) public @interface RequestState {} /** * Indicating currently no data networks can satisfy this network request. */ public static final int REQUEST_STATE_UNSATISFIED = 0; /** * Indicating this request is already satisfied. It must have an attached network (which could * be in any state, including disconnecting). Also note this does not mean the network request * is satisfied in telephony layer. Whether the network request is finally satisfied or not is * determined at the connectivity service layer. */ public static final int REQUEST_STATE_SATISFIED = 1; /** @hide */ @IntDef(flag = true, prefix = { "CAPABILITY_ATTRIBUTE_" }, value = { CAPABILITY_ATTRIBUTE_NONE, CAPABILITY_ATTRIBUTE_APN_SETTING, CAPABILITY_ATTRIBUTE_TRAFFIC_DESCRIPTOR_DNN, CAPABILITY_ATTRIBUTE_TRAFFIC_DESCRIPTOR_OS_APP_ID, }) @Retention(RetentionPolicy.SOURCE) public @interface NetCapabilityAttribute {} /** Network capability attribute none. */ public static final int CAPABILITY_ATTRIBUTE_NONE = 0; /** * The network capability should result in filling {@link ApnSetting} in {@link DataProfile}. */ public static final int CAPABILITY_ATTRIBUTE_APN_SETTING = 1; /** The network capability should result in filling DNN in {@link TrafficDescriptor}. */ public static final int CAPABILITY_ATTRIBUTE_TRAFFIC_DESCRIPTOR_DNN = 1 << 1; /** The network capability should result in filling OS/APP id in {@link TrafficDescriptor}. */ public static final int CAPABILITY_ATTRIBUTE_TRAFFIC_DESCRIPTOR_OS_APP_ID = 1 << 2; /** * Describes the attributes of network capabilities. Different capabilities can be translated * to different fields in {@link DataProfile}, or might be expanded to support special actions * in telephony in the future. */ private static final Map CAPABILITY_ATTRIBUTE_MAP = Map.ofEntries( new SimpleImmutableEntry<>(NetworkCapabilities.NET_CAPABILITY_MMS, CAPABILITY_ATTRIBUTE_APN_SETTING | CAPABILITY_ATTRIBUTE_TRAFFIC_DESCRIPTOR_DNN), new SimpleImmutableEntry<>(NetworkCapabilities.NET_CAPABILITY_SUPL, CAPABILITY_ATTRIBUTE_APN_SETTING | CAPABILITY_ATTRIBUTE_TRAFFIC_DESCRIPTOR_DNN), new SimpleImmutableEntry<>(NetworkCapabilities.NET_CAPABILITY_DUN, CAPABILITY_ATTRIBUTE_APN_SETTING | CAPABILITY_ATTRIBUTE_TRAFFIC_DESCRIPTOR_DNN), new SimpleImmutableEntry<>(NetworkCapabilities.NET_CAPABILITY_FOTA, CAPABILITY_ATTRIBUTE_APN_SETTING | CAPABILITY_ATTRIBUTE_TRAFFIC_DESCRIPTOR_DNN), new SimpleImmutableEntry<>(NetworkCapabilities.NET_CAPABILITY_IMS, CAPABILITY_ATTRIBUTE_APN_SETTING | CAPABILITY_ATTRIBUTE_TRAFFIC_DESCRIPTOR_DNN), new SimpleImmutableEntry<>(NetworkCapabilities.NET_CAPABILITY_CBS, CAPABILITY_ATTRIBUTE_APN_SETTING | CAPABILITY_ATTRIBUTE_TRAFFIC_DESCRIPTOR_DNN | CAPABILITY_ATTRIBUTE_TRAFFIC_DESCRIPTOR_OS_APP_ID), new SimpleImmutableEntry<>(NetworkCapabilities.NET_CAPABILITY_XCAP, CAPABILITY_ATTRIBUTE_APN_SETTING | CAPABILITY_ATTRIBUTE_TRAFFIC_DESCRIPTOR_DNN), new SimpleImmutableEntry<>(NetworkCapabilities.NET_CAPABILITY_EIMS, CAPABILITY_ATTRIBUTE_APN_SETTING | CAPABILITY_ATTRIBUTE_TRAFFIC_DESCRIPTOR_DNN), new SimpleImmutableEntry<>(NetworkCapabilities.NET_CAPABILITY_INTERNET, CAPABILITY_ATTRIBUTE_APN_SETTING | CAPABILITY_ATTRIBUTE_TRAFFIC_DESCRIPTOR_DNN), new SimpleImmutableEntry<>(NetworkCapabilities.NET_CAPABILITY_MCX, CAPABILITY_ATTRIBUTE_APN_SETTING | CAPABILITY_ATTRIBUTE_TRAFFIC_DESCRIPTOR_DNN), new SimpleImmutableEntry<>(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE, CAPABILITY_ATTRIBUTE_APN_SETTING | CAPABILITY_ATTRIBUTE_TRAFFIC_DESCRIPTOR_DNN | CAPABILITY_ATTRIBUTE_TRAFFIC_DESCRIPTOR_OS_APP_ID), new SimpleImmutableEntry<>(NetworkCapabilities.NET_CAPABILITY_VSIM, CAPABILITY_ATTRIBUTE_APN_SETTING | CAPABILITY_ATTRIBUTE_TRAFFIC_DESCRIPTOR_DNN), new SimpleImmutableEntry<>(NetworkCapabilities.NET_CAPABILITY_BIP, CAPABILITY_ATTRIBUTE_APN_SETTING | CAPABILITY_ATTRIBUTE_TRAFFIC_DESCRIPTOR_DNN), new SimpleImmutableEntry<>(NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_LATENCY, CAPABILITY_ATTRIBUTE_TRAFFIC_DESCRIPTOR_OS_APP_ID), new SimpleImmutableEntry<>(NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_BANDWIDTH, CAPABILITY_ATTRIBUTE_TRAFFIC_DESCRIPTOR_OS_APP_ID), new SimpleImmutableEntry<>(NetworkCapabilities.NET_CAPABILITY_RCS, CAPABILITY_ATTRIBUTE_APN_SETTING | CAPABILITY_ATTRIBUTE_TRAFFIC_DESCRIPTOR_DNN) ); /** The phone instance. */ private final @NonNull Phone mPhone; /** * Native network request from the clients. See {@link NetworkRequest}; */ private final @NonNull NetworkRequest mNativeNetworkRequest; /** * The attributes of the network capabilities in this network request. This describes how this * network request can be translated to different fields in {@link DataProfile} or perform * special actions in telephony. */ private final @NetCapabilityAttribute int mCapabilitiesAttributes; /** * Priority of the network request. The network request has higher priority will be satisfied * first than lower priority ones. */ private int mPriority; /** * Data config manager for retrieving data config. */ private final @NonNull DataConfigManager mDataConfigManager; /** * The attached data network. Note that the data network could be in any state. {@code null} * indicates this network request is not satisfied. */ private @Nullable DataNetwork mAttachedDataNetwork; /** * The state of the network request. * * @see #REQUEST_STATE_UNSATISFIED * @see #REQUEST_STATE_SATISFIED */ // This is not a boolean because there might be more states in the future. private @RequestState int mState; /** The timestamp when this network request enters telephony. */ private final @ElapsedRealtimeLong long mCreatedTimeMillis; /** The data evaluation result. */ private @Nullable DataEvaluation mEvaluation; /** Feature flag. */ private final @NonNull FeatureFlags mFeatureFlags; /** * Constructor * * @param request The native network request from the clients. * @param phone The phone instance * @param featureFlags The feature flag */ public TelephonyNetworkRequest(@NonNull NetworkRequest request, @NonNull Phone phone, @NonNull FeatureFlags featureFlags) { mPhone = phone; mNativeNetworkRequest = request; mFeatureFlags = featureFlags; int capabilitiesAttributes = CAPABILITY_ATTRIBUTE_NONE; for (int networkCapability : mNativeNetworkRequest.getCapabilities()) { capabilitiesAttributes |= CAPABILITY_ATTRIBUTE_MAP.getOrDefault( networkCapability, CAPABILITY_ATTRIBUTE_NONE); } mCapabilitiesAttributes = capabilitiesAttributes; mPriority = 0; mAttachedDataNetwork = null; // When the request was first created, it is in active state so we can actively attempt // to satisfy it. mState = REQUEST_STATE_UNSATISFIED; mCreatedTimeMillis = SystemClock.elapsedRealtime(); mDataConfigManager = phone.getDataNetworkController().getDataConfigManager(); updatePriority(); } /** * @see NetworkRequest#getNetworkSpecifier() */ public @Nullable NetworkSpecifier getNetworkSpecifier() { return mNativeNetworkRequest.getNetworkSpecifier(); } /** * @see NetworkRequest#getCapabilities() */ public @NonNull @NetCapability int[] getCapabilities() { return mNativeNetworkRequest.getCapabilities(); } /** * @see NetworkRequest#hasCapability(int) */ public boolean hasCapability(@NetCapability int capability) { return mNativeNetworkRequest.hasCapability(capability); } /** * @see NetworkRequest#canBeSatisfiedBy(NetworkCapabilities) */ public boolean canBeSatisfiedBy(@Nullable NetworkCapabilities nc) { return mNativeNetworkRequest.canBeSatisfiedBy(nc); } /** * Check if the request's capabilities have certain attributes. * * @param capabilitiesAttributes The attributes to check. * @return {@code true} if the capabilities have provided attributes. * * @see NetCapabilityAttribute */ public boolean hasAttribute(@NetCapabilityAttribute int capabilitiesAttributes) { return (mCapabilitiesAttributes & capabilitiesAttributes) == capabilitiesAttributes; } /** * Check if this network request can be satisfied by a data profile. * * @param dataProfile The data profile to check. * @return {@code true} if this network request can be satisfied by the data profile. */ public boolean canBeSatisfiedBy(@NonNull DataProfile dataProfile) { // If the network request can be translated to OS/App id, then check if the data profile's // OS/App id can satisfy it. if (hasAttribute(CAPABILITY_ATTRIBUTE_TRAFFIC_DESCRIPTOR_OS_APP_ID) && getOsAppId() != null) { // The network request has traffic descriptor type capabilities. Match the traffic // descriptor. if (dataProfile.getTrafficDescriptor() != null && Arrays.equals(getOsAppId().getBytes(), dataProfile.getTrafficDescriptor().getOsAppId())) { return true; } } // If the network request can be translated to APN setting or DNN in traffic descriptor, // then check if the data profile's APN setting can satisfy it. if ((hasAttribute(CAPABILITY_ATTRIBUTE_APN_SETTING) || hasAttribute(CAPABILITY_ATTRIBUTE_TRAFFIC_DESCRIPTOR_DNN)) && dataProfile.getApnSetting() != null) { if (mFeatureFlags.carrierEnabledSatelliteFlag()) { if (mNativeNetworkRequest.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) && !mNativeNetworkRequest.hasTransport( NetworkCapabilities.TRANSPORT_SATELLITE)) { if (Arrays.stream(getCapabilities()).noneMatch(mDataConfigManager .getForcedCellularTransportCapabilities()::contains)) { // If the request is explicitly for the cellular, then the data profile // needs to support cellular. if (!dataProfile.getApnSetting().isForInfrastructure( ApnSetting.INFRASTRUCTURE_CELLULAR)) { return false; } } } else if (mNativeNetworkRequest.hasTransport( NetworkCapabilities.TRANSPORT_SATELLITE) && !mNativeNetworkRequest.hasTransport( NetworkCapabilities.TRANSPORT_CELLULAR)) { // If the request is explicitly for the satellite, then the data profile needs // to support satellite. if (!dataProfile.getApnSetting().isForInfrastructure( ApnSetting.INFRASTRUCTURE_SATELLITE)) { return false; } } } // Fallback to the legacy APN type matching. List apnTypes = Arrays.stream(getCapabilities()).boxed() .map(DataUtils::networkCapabilityToApnType) .filter(apnType -> apnType != ApnSetting.TYPE_NONE) .collect(Collectors.toList()); // In case of enterprise network request, the network request will have internet, // but APN type will not have default type as the enterprise apn should not be used // as default network. Ignore default type of the network request if it // has enterprise type as well. This will make sure the network request with // internet and enterprise will be satisfied with data profile with enterprise at the // same time default network request will not get satisfied with enterprise data // profile. // TODO b/232264746 if (apnTypes.contains(ApnSetting.TYPE_ENTERPRISE)) { apnTypes.remove((Integer) ApnSetting.TYPE_DEFAULT); } return apnTypes.stream().allMatch(dataProfile.getApnSetting()::canHandleType); } return false; } /** * Get the priority of the network request. * * @return The priority from 0 to 100. 100 indicates the highest priority. */ public int getPriority() { return mPriority; } /** * Update the priority from data config manager. */ public void updatePriority() { mPriority = Arrays.stream(mNativeNetworkRequest.getCapabilities()) .map(mDataConfigManager::getNetworkCapabilityPriority) .max() .orElse(0); } /** * Get the network capability which is APN-type based from the network request. If there are * multiple APN types capability, the highest priority one will be returned. * * @return The highest priority APN type based network capability from this network request. -1 * if there is no APN type capabilities in this network request. */ public @NetCapability int getApnTypeNetworkCapability() { if (!hasAttribute(CAPABILITY_ATTRIBUTE_APN_SETTING)) return -1; return Arrays.stream(getCapabilities()).boxed() .filter(cap -> DataUtils.networkCapabilityToApnType(cap) != ApnSetting.TYPE_NONE) .max(Comparator.comparingInt(mDataConfigManager::getNetworkCapabilityPriority)) .orElse(-1); } /** * @return The native network request. */ public @NonNull NetworkRequest getNativeNetworkRequest() { return mNativeNetworkRequest; } /** * Set the attached data network. * * @param dataNetwork The data network. */ public void setAttachedNetwork(@NonNull DataNetwork dataNetwork) { mAttachedDataNetwork = dataNetwork; } /** * @return The attached network. {@code null} indicates the request is not attached to any * network (i.e. the request is unsatisfied). */ public @Nullable DataNetwork getAttachedNetwork() { return mAttachedDataNetwork; } /** * Set the state of the network request. * * @param state The state. */ public void setState(@RequestState int state) { mState = state; } /** * @return The state of the network request. */ public @RequestState int getState() { return mState; } /** * Set the data evaluation result. * * @param evaluation The data evaluation result. */ public void setEvaluation(@NonNull DataEvaluation evaluation) { mEvaluation = evaluation; } /** * Get the capability differentiator from the network request. Some capabilities * (e.g. {@link NetworkCapabilities#NET_CAPABILITY_ENTERPRISE} could support more than one * traffic (e.g. "ENTERPRISE2", "ENTERPRISE3"). This method returns that differentiator. * * @return The differentiator. 0 if not found. */ public int getCapabilityDifferentiator() { if (hasCapability(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE)) { int[] ids = mNativeNetworkRequest.getEnterpriseIds(); // No need to verify the range of the id. It has been done in NetworkCapabilities. if (ids.length > 0) return ids[0]; } return 0; } /** * @return {@code true} if this network request can result in bringing up a metered network. */ public boolean isMeteredRequest() { return mDataConfigManager.isAnyMeteredCapability( getCapabilities(), mPhone.getServiceState().getDataRoaming()); } /** * Get Os/App id from the network request. * * @return Os/App id. {@code null} if the request does not have traffic descriptor based network * capabilities. */ public @Nullable OsAppId getOsAppId() { if (!hasAttribute(CAPABILITY_ATTRIBUTE_TRAFFIC_DESCRIPTOR_OS_APP_ID)) return null; // We do not support multiple network capabilities translated to Os/App id at this time. // If someday this needs to be done, we need to expand TrafficDescriptor to support // connection capabilities instead of using Os/App id to do the work. int networkCapability = Arrays.stream(getCapabilities()).boxed() .filter(cap -> (CAPABILITY_ATTRIBUTE_MAP.getOrDefault( cap, CAPABILITY_ATTRIBUTE_NONE) & CAPABILITY_ATTRIBUTE_TRAFFIC_DESCRIPTOR_OS_APP_ID) != 0) .findFirst() .orElse(-1); if (networkCapability == -1) return null; int differentiator = getCapabilityDifferentiator(); if (differentiator > 0) { return new OsAppId(OsAppId.ANDROID_OS_ID, DataUtils.networkCapabilityToString(networkCapability), differentiator); } else { return new OsAppId(OsAppId.ANDROID_OS_ID, DataUtils.networkCapabilityToString(networkCapability)); } } /** * Convert the telephony request state to string. * * @param state The request state. * @return The request state in string format. */ private static @NonNull String requestStateToString( @TelephonyNetworkRequest.RequestState int state) { switch (state) { case TelephonyNetworkRequest.REQUEST_STATE_UNSATISFIED: return "UNSATISFIED"; case TelephonyNetworkRequest.REQUEST_STATE_SATISFIED: return "SATISFIED"; default: return "UNKNOWN(" + state + ")"; } } @Override public String toString() { return "[" + mNativeNetworkRequest.toString() + ", mPriority=" + mPriority + ", state=" + requestStateToString(mState) + ", mAttachedDataNetwork=" + (mAttachedDataNetwork != null ? mAttachedDataNetwork.name() : null) + ", isMetered=" + isMeteredRequest() + ", created time=" + DataUtils.elapsedTimeToString(mCreatedTimeMillis) + ", evaluation result=" + mEvaluation + "]"; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; TelephonyNetworkRequest that = (TelephonyNetworkRequest) o; // Only compare the native network request. return mNativeNetworkRequest.equals(that.mNativeNetworkRequest); } @Override public int hashCode() { // Only use the native network request's hash code. return mNativeNetworkRequest.hashCode(); } }