Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 09d69656 authored by Sangyun Yun's avatar Sangyun Yun Committed by Android (Google) Code Review
Browse files

Merge "Add APIs for network validation feature." into main

parents 45d0bd1e 978f45d3
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -44755,10 +44755,16 @@ package android.telephony {
    method public int getLastCauseCode();
    method @Nullable public android.net.LinkProperties getLinkProperties();
    method public int getNetworkType();
    method @FlaggedApi("com.android.internal.telephony.flags.network_validation") public int getNetworkValidationStatus();
    method public int getState();
    method public int getTransportType();
    method public void writeToParcel(@NonNull android.os.Parcel, int);
    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.PreciseDataConnectionState> CREATOR;
    field @FlaggedApi("com.android.internal.telephony.flags.network_validation") public static final int NETWORK_VALIDATION_FAILURE = 4; // 0x4
    field @FlaggedApi("com.android.internal.telephony.flags.network_validation") public static final int NETWORK_VALIDATION_IN_PROGRESS = 2; // 0x2
    field @FlaggedApi("com.android.internal.telephony.flags.network_validation") public static final int NETWORK_VALIDATION_NOT_REQUESTED = 1; // 0x1
    field @FlaggedApi("com.android.internal.telephony.flags.network_validation") public static final int NETWORK_VALIDATION_SUCCESS = 3; // 0x3
    field @FlaggedApi("com.android.internal.telephony.flags.network_validation") public static final int NETWORK_VALIDATION_UNSUPPORTED = 0; // 0x0
  }
  public final class RadioAccessSpecifier implements android.os.Parcelable {
+4 −0
Original line number Diff line number Diff line
@@ -14837,6 +14837,7 @@ package android.telephony.data {
    method @Deprecated public int getMtu();
    method public int getMtuV4();
    method public int getMtuV6();
    method @FlaggedApi("com.android.internal.telephony.flags.network_validation") public int getNetworkValidationStatus();
    method @NonNull public java.util.List<java.net.InetAddress> getPcscfAddresses();
    method public int getPduSessionId();
    method public int getProtocolType();
@@ -14873,6 +14874,7 @@ package android.telephony.data {
    method @Deprecated @NonNull public android.telephony.data.DataCallResponse.Builder setMtu(int);
    method @NonNull public android.telephony.data.DataCallResponse.Builder setMtuV4(int);
    method @NonNull public android.telephony.data.DataCallResponse.Builder setMtuV6(int);
    method @FlaggedApi("com.android.internal.telephony.flags.network_validation") @NonNull public android.telephony.data.DataCallResponse.Builder setNetworkValidationStatus(int);
    method @NonNull public android.telephony.data.DataCallResponse.Builder setPcscfAddresses(@NonNull java.util.List<java.net.InetAddress>);
    method @NonNull public android.telephony.data.DataCallResponse.Builder setPduSessionId(@IntRange(from=android.telephony.data.DataCallResponse.PDU_SESSION_ID_NOT_SET, to=15) int);
    method @NonNull public android.telephony.data.DataCallResponse.Builder setProtocolType(int);
@@ -14952,6 +14954,7 @@ package android.telephony.data {
    method public final void notifyDataCallListChanged(java.util.List<android.telephony.data.DataCallResponse>);
    method public final void notifyDataProfileUnthrottled(@NonNull android.telephony.data.DataProfile);
    method public void requestDataCallList(@NonNull android.telephony.data.DataServiceCallback);
    method @FlaggedApi("com.android.internal.telephony.flags.network_validation") public void requestValidation(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
    method public void setDataProfile(@NonNull java.util.List<android.telephony.data.DataProfile>, boolean, @NonNull android.telephony.data.DataServiceCallback);
    method public void setInitialAttachApn(@NonNull android.telephony.data.DataProfile, boolean, @NonNull android.telephony.data.DataServiceCallback);
    method public void setupDataCall(int, @NonNull android.telephony.data.DataProfile, boolean, boolean, int, @Nullable android.net.LinkProperties, @NonNull android.telephony.data.DataServiceCallback);
@@ -15013,6 +15016,7 @@ package android.telephony.data {
    method public final int getSlotIndex();
    method public void reportEmergencyDataNetworkPreferredTransportChanged(int);
    method public void reportThrottleStatusChanged(@NonNull java.util.List<android.telephony.data.ThrottleStatus>);
    method @FlaggedApi("com.android.internal.telephony.flags.network_validation") public void requestNetworkValidation(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
    method public final void updateQualifiedNetworkTypes(int, @NonNull java.util.List<java.lang.Integer>);
  }
+113 −5
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package android.telephony;

import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -37,8 +39,11 @@ import android.telephony.data.ApnSetting;
import android.telephony.data.DataCallResponse;
import android.telephony.data.Qos;

import com.android.internal.telephony.flags.Flags;
import com.android.internal.telephony.util.TelephonyUtils;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;


@@ -66,6 +71,53 @@ public final class PreciseDataConnectionState implements Parcelable {
    private final LinkProperties mLinkProperties;
    private final ApnSetting mApnSetting;
    private final Qos mDefaultQos;
    private final @NetworkValidationStatus int mNetworkValidationStatus;

    /** @hide */
    @IntDef(prefix = "NETWORK_VALIDATION_", value = {
            NETWORK_VALIDATION_UNSUPPORTED,
            NETWORK_VALIDATION_NOT_REQUESTED,
            NETWORK_VALIDATION_IN_PROGRESS,
            NETWORK_VALIDATION_SUCCESS,
            NETWORK_VALIDATION_FAILURE,
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface NetworkValidationStatus {}

    /**
     * Unsupported. The unsupported state is used when the data network cannot support the network
     * validation function for the current data connection state.
     */
    @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
    public static final int NETWORK_VALIDATION_UNSUPPORTED = 0;

    /**
     * Not Requested. The not requested status is used when the data network supports the network
     * validation function, but no network validation is being performed yet.
     */
    @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
    public static final int NETWORK_VALIDATION_NOT_REQUESTED = 1;

    /**
     * In progress. The in progress state is used when the network validation process for the data
     * network is in progress. This state is followed by either success or failure.
     */
    @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
    public static final int NETWORK_VALIDATION_IN_PROGRESS = 2;

    /**
     * Success. The Success status is used when network validation has been completed for the data
     * network and the result is successful.
     */
    @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
    public static final int NETWORK_VALIDATION_SUCCESS = 3;

    /**
     * Failure. The Failure status is used when network validation has been completed for the data
     * network and the result is failure.
     */
    @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
    public static final int NETWORK_VALIDATION_FAILURE = 4;

    /**
     * Constructor
@@ -87,7 +139,7 @@ public final class PreciseDataConnectionState implements Parcelable {
                        .setApnTypeBitmask(apnTypes)
                        .setApnName(apn)
                        .setEntryName(apn)
                        .build(), null);
                        .build(), null, NETWORK_VALIDATION_UNSUPPORTED);
    }


@@ -109,7 +161,8 @@ public final class PreciseDataConnectionState implements Parcelable {
    private PreciseDataConnectionState(@TransportType int transportType, int id,
            @DataState int state, @NetworkType int networkType,
            @Nullable LinkProperties linkProperties, @DataFailureCause int failCause,
            @Nullable ApnSetting apnSetting, @Nullable Qos defaultQos) {
            @Nullable ApnSetting apnSetting, @Nullable Qos defaultQos,
            @NetworkValidationStatus int networkValidationStatus) {
        mTransportType = transportType;
        mId = id;
        mState = state;
@@ -118,6 +171,7 @@ public final class PreciseDataConnectionState implements Parcelable {
        mFailCause = failCause;
        mApnSetting = apnSetting;
        mDefaultQos = defaultQos;
        mNetworkValidationStatus = networkValidationStatus;
    }

    /**
@@ -140,6 +194,7 @@ public final class PreciseDataConnectionState implements Parcelable {
        mDefaultQos = in.readParcelable(
                Qos.class.getClassLoader(),
                android.telephony.data.Qos.class);
        mNetworkValidationStatus = in.readInt();
    }

    /**
@@ -289,6 +344,16 @@ public final class PreciseDataConnectionState implements Parcelable {
        return mDefaultQos;
    }

    /**
     * Returns the network validation state.
     *
     * @return the network validation status of the data call
     */
    @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
    public @NetworkValidationStatus int getNetworkValidationStatus() {
        return mNetworkValidationStatus;
    }

    @Override
    public int describeContents() {
        return 0;
@@ -304,6 +369,7 @@ public final class PreciseDataConnectionState implements Parcelable {
        out.writeInt(mFailCause);
        out.writeParcelable(mApnSetting, flags);
        out.writeParcelable(mDefaultQos, flags);
        out.writeInt(mNetworkValidationStatus);
    }

    public static final @NonNull Parcelable.Creator<PreciseDataConnectionState> CREATOR
@@ -321,7 +387,7 @@ public final class PreciseDataConnectionState implements Parcelable {
    @Override
    public int hashCode() {
        return Objects.hash(mTransportType, mId, mState, mNetworkType, mFailCause,
                mLinkProperties, mApnSetting, mDefaultQos);
                mLinkProperties, mApnSetting, mDefaultQos, mNetworkValidationStatus);
    }


@@ -337,7 +403,8 @@ public final class PreciseDataConnectionState implements Parcelable {
                && mFailCause == that.mFailCause
                && Objects.equals(mLinkProperties, that.mLinkProperties)
                && Objects.equals(mApnSetting, that.mApnSetting)
                && Objects.equals(mDefaultQos, that.mDefaultQos);
                && Objects.equals(mDefaultQos, that.mDefaultQos)
                && mNetworkValidationStatus == that.mNetworkValidationStatus;
    }

    @NonNull
@@ -354,10 +421,33 @@ public final class PreciseDataConnectionState implements Parcelable {
        sb.append(", link properties: " + mLinkProperties);
        sb.append(", default QoS: " + mDefaultQos);
        sb.append(", fail cause: " + DataFailCause.toString(mFailCause));
        sb.append(", network validation status: "
                + networkValidationStatusToString(mNetworkValidationStatus));

        return sb.toString();
    }

    /**
     * Convert a network validation status to string.
     *
     * @param networkValidationStatus network validation status.
     * @return string of validation status.
     *
     * @hide
     */
    @NonNull
    public static String networkValidationStatusToString(
            @NetworkValidationStatus int networkValidationStatus) {
        switch (networkValidationStatus) {
            case NETWORK_VALIDATION_UNSUPPORTED: return "unsupported";
            case NETWORK_VALIDATION_NOT_REQUESTED: return "not requested";
            case NETWORK_VALIDATION_IN_PROGRESS: return "in progress";
            case NETWORK_VALIDATION_SUCCESS: return "success";
            case NETWORK_VALIDATION_FAILURE: return "failure";
            default: return Integer.toString(networkValidationStatus);
        }
    }

    /**
     * {@link PreciseDataConnectionState} builder
     *
@@ -394,6 +484,10 @@ public final class PreciseDataConnectionState implements Parcelable {
        /** The Default QoS for this EPS/5GS bearer or null otherwise */
        private @Nullable Qos mDefaultQos;

        /** The network validation status for the data connection. */
        private @NetworkValidationStatus int mNetworkValidationStatus =
                NETWORK_VALIDATION_UNSUPPORTED;

        /**
         * Set the transport type of the data connection.
         *
@@ -485,6 +579,19 @@ public final class PreciseDataConnectionState implements Parcelable {
            return this;
        }

        /**
         * Set the network validation state for the data connection.
         *
         * @param networkValidationStatus the network validation status of the data call
         * @return The builder
         */
        @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
        public @NonNull Builder setNetworkValidationStatus(
                @NetworkValidationStatus int networkValidationStatus) {
            mNetworkValidationStatus = networkValidationStatus;
            return this;
        }

        /**
         * Build the {@link PreciseDataConnectionState} instance.
         *
@@ -492,7 +599,8 @@ public final class PreciseDataConnectionState implements Parcelable {
         */
        public PreciseDataConnectionState build() {
            return new PreciseDataConnectionState(mTransportType, mId, mState, mNetworkType,
                    mLinkProperties, mFailCause, mApnSetting, mDefaultQos);
                    mLinkProperties, mFailCause, mApnSetting, mDefaultQos,
                    mNetworkValidationStatus);
        }
    }
}
+46 −6
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@

package android.telephony.data;

import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -27,9 +28,11 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.telephony.Annotation.DataFailureCause;
import android.telephony.DataFailCause;
import android.telephony.PreciseDataConnectionState;
import android.telephony.data.ApnSetting.ProtocolType;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.flags.Flags;
import com.android.internal.util.Preconditions;

import java.lang.annotation.Retention;
@@ -123,7 +126,6 @@ public final class DataCallResponse implements Parcelable {
     * Indicates that the pdu session id is not set.
     */
    public static final int PDU_SESSION_ID_NOT_SET = 0;

    private final @DataFailureCause int mCause;
    private final long mSuggestedRetryTime;
    private final int mId;
@@ -143,6 +145,7 @@ public final class DataCallResponse implements Parcelable {
    private final List<QosBearerSession> mQosBearerSessions;
    private final NetworkSliceInfo mSliceInfo;
    private final List<TrafficDescriptor> mTrafficDescriptors;
    private final @PreciseDataConnectionState.NetworkValidationStatus int mNetworkValidationStatus;

    /**
     * @param cause Data call fail cause. {@link DataFailCause#NONE} indicates no error.
@@ -185,7 +188,8 @@ public final class DataCallResponse implements Parcelable {
                HANDOVER_FAILURE_MODE_LEGACY, PDU_SESSION_ID_NOT_SET,
                null /* defaultQos */, Collections.emptyList() /* qosBearerSessions */,
                null /* sliceInfo */,
                Collections.emptyList() /* trafficDescriptors */);
                Collections.emptyList(), /* trafficDescriptors */
                PreciseDataConnectionState.NETWORK_VALIDATION_UNSUPPORTED);
    }

    private DataCallResponse(@DataFailureCause int cause, long suggestedRetryTime, int id,
@@ -196,7 +200,8 @@ public final class DataCallResponse implements Parcelable {
            @HandoverFailureMode int handoverFailureMode, int pduSessionId,
            @Nullable Qos defaultQos, @NonNull List<QosBearerSession> qosBearerSessions,
            @Nullable NetworkSliceInfo sliceInfo,
            @NonNull List<TrafficDescriptor> trafficDescriptors) {
            @NonNull List<TrafficDescriptor> trafficDescriptors,
            @PreciseDataConnectionState.NetworkValidationStatus int networkValidationStatus) {
        mCause = cause;
        mSuggestedRetryTime = suggestedRetryTime;
        mId = id;
@@ -216,6 +221,7 @@ public final class DataCallResponse implements Parcelable {
        mQosBearerSessions = new ArrayList<>(qosBearerSessions);
        mSliceInfo = sliceInfo;
        mTrafficDescriptors = new ArrayList<>(trafficDescriptors);
        mNetworkValidationStatus = networkValidationStatus;

        if (mLinkStatus == LINK_STATUS_ACTIVE
                || mLinkStatus == LINK_STATUS_DORMANT) {
@@ -270,6 +276,7 @@ public final class DataCallResponse implements Parcelable {
        source.readList(mTrafficDescriptors,
                TrafficDescriptor.class.getClassLoader(),
                android.telephony.data.TrafficDescriptor.class);
        mNetworkValidationStatus = source.readInt();
    }

    /**
@@ -442,6 +449,17 @@ public final class DataCallResponse implements Parcelable {
        return Collections.unmodifiableList(mTrafficDescriptors);
    }

    /**
     * Return the network validation status that was initiated by {@link
     * DataService.DataServiceProvider#requestValidation}
     *
     * @return The network validation status of data connection.
     */
    @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
    public @PreciseDataConnectionState.NetworkValidationStatus int getNetworkValidationStatus() {
        return mNetworkValidationStatus;
    }

    @NonNull
    @Override
    public String toString() {
@@ -466,6 +484,8 @@ public final class DataCallResponse implements Parcelable {
           .append(" qosBearerSessions=").append(mQosBearerSessions)
           .append(" sliceInfo=").append(mSliceInfo)
           .append(" trafficDescriptors=").append(mTrafficDescriptors)
           .append(" networkValidationStatus=").append(PreciseDataConnectionState
                        .networkValidationStatusToString(mNetworkValidationStatus))
           .append("}");
        return sb.toString();
    }
@@ -504,7 +524,8 @@ public final class DataCallResponse implements Parcelable {
                && mQosBearerSessions.containsAll(other.mQosBearerSessions) // non-null
                && Objects.equals(mSliceInfo, other.mSliceInfo)
                && mTrafficDescriptors.size() == other.mTrafficDescriptors.size() // non-null
                && mTrafficDescriptors.containsAll(other.mTrafficDescriptors); // non-null
                && mTrafficDescriptors.containsAll(other.mTrafficDescriptors) // non-null
                && mNetworkValidationStatus == other.mNetworkValidationStatus;
    }

    @Override
@@ -513,7 +534,7 @@ public final class DataCallResponse implements Parcelable {
                mInterfaceName, Set.copyOf(mAddresses), Set.copyOf(mDnsAddresses),
                Set.copyOf(mGatewayAddresses), Set.copyOf(mPcscfAddresses), mMtu, mMtuV4, mMtuV6,
                mHandoverFailureMode, mPduSessionId, mDefaultQos, Set.copyOf(mQosBearerSessions),
                mSliceInfo, Set.copyOf(mTrafficDescriptors));
                mSliceInfo, Set.copyOf(mTrafficDescriptors), mNetworkValidationStatus);
    }

    @Override
@@ -542,6 +563,7 @@ public final class DataCallResponse implements Parcelable {
        dest.writeList(mQosBearerSessions);
        dest.writeParcelable(mSliceInfo, flags);
        dest.writeList(mTrafficDescriptors);
        dest.writeInt(mNetworkValidationStatus);
    }

    public static final @android.annotation.NonNull Parcelable.Creator<DataCallResponse> CREATOR =
@@ -629,6 +651,9 @@ public final class DataCallResponse implements Parcelable {

        private List<TrafficDescriptor> mTrafficDescriptors = new ArrayList<>();

        private @PreciseDataConnectionState.NetworkValidationStatus int mNetworkValidationStatus =
                PreciseDataConnectionState.NETWORK_VALIDATION_UNSUPPORTED;

        /**
         * Default constructor for Builder.
         */
@@ -904,6 +929,20 @@ public final class DataCallResponse implements Parcelable {
            return this;
        }

        /**
         * Set the network validation status that corresponds to the state of the network validation
         * request started by {@link DataService.DataServiceProvider#requestValidation}
         *
         * @param status The network validation status.
         * @return The same instance of the builder.
         */
        @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
        public @NonNull Builder setNetworkValidationStatus(
                @PreciseDataConnectionState.NetworkValidationStatus int status) {
            mNetworkValidationStatus = status;
            return this;
        }

        /**
         * Build the DataCallResponse.
         *
@@ -913,7 +952,8 @@ public final class DataCallResponse implements Parcelable {
            return new DataCallResponse(mCause, mSuggestedRetryTime, mId, mLinkStatus,
                    mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses,
                    mPcscfAddresses, mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId,
                    mDefaultQos, mQosBearerSessions, mSliceInfo, mTrafficDescriptors);
                    mDefaultQos, mQosBearerSessions, mSliceInfo, mTrafficDescriptors,
                    mNetworkValidationStatus);
        }
    }
}
+81 −0
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package android.telephony.data;

import android.annotation.CallbackExecutor;
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -26,6 +28,7 @@ import android.app.Service;
import android.content.Intent;
import android.net.LinkProperties;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
@@ -36,6 +39,9 @@ import android.util.Log;
import android.util.SparseArray;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.IIntegerConsumer;
import com.android.internal.telephony.flags.Flags;
import com.android.internal.util.FunctionalUtils;
import com.android.telephony.Rlog;

import java.lang.annotation.Retention;
@@ -44,6 +50,8 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.function.Consumer;

/**
 * Base class of data service. Services that extend DataService must register the service in
@@ -113,11 +121,14 @@ public abstract class DataService extends Service {
    private static final int DATA_SERVICE_REQUEST_REGISTER_APN_UNTHROTTLED             = 14;
    private static final int DATA_SERVICE_REQUEST_UNREGISTER_APN_UNTHROTTLED           = 15;
    private static final int DATA_SERVICE_INDICATION_APN_UNTHROTTLED                   = 16;
    private static final int DATA_SERVICE_REQUEST_VALIDATION                           = 17;

    private final HandlerThread mHandlerThread;

    private final DataServiceHandler mHandler;

    private final Executor mHandlerExecutor;

    private final SparseArray<DataServiceProvider> mServiceMap = new SparseArray<>();

    /** @hide */
@@ -379,6 +390,43 @@ public abstract class DataService extends Service {
            }
        }

        /**
         * Request validation check to see if the network is working properly for a given data call.
         *
         * <p>This request is completed immediately after submitting the request to the data service
         * provider and receiving {@link DataServiceCallback.ResultCode}, and progress status or
         * validation results are notified through {@link
         * DataCallResponse#getNetworkValidationStatus}.
         *
         * <p> If the network validation request is submitted successfully, {@link
         * DataServiceCallback#RESULT_SUCCESS} is passed to {@code resultCodeCallback}. If the
         * network validation feature is not supported by the data service provider itself, {@link
         * DataServiceCallback#RESULT_ERROR_UNSUPPORTED} is passed to {@code resultCodeCallback}.
         * See {@link DataServiceCallback.ResultCode} for the type of response that indicates
         * whether the request was successfully submitted or had an error.
         *
         * <p>In response to this network validation request, providers can validate the data call
         * in their own way. For example, in IWLAN, the DPD (Dead Peer Detection) can be used as a
         * tool to check whether a data call is alive.
         *
         * @param cid The identifier of the data call which is provided in {@link DataCallResponse}
         * @param executor The callback executor for the response.
         * @param resultCodeCallback Listener for the {@link DataServiceCallback.ResultCode} that
         *     request validation to the DataService and checks if the request has been submitted.
         */
        @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
        public void requestValidation(int cid,
                @NonNull @CallbackExecutor Executor executor,
                @NonNull @DataServiceCallback.ResultCode Consumer<Integer> resultCodeCallback) {
            Objects.requireNonNull(executor, "executor cannot be null");
            Objects.requireNonNull(resultCodeCallback, "resultCodeCallback cannot be null");

            Log.d(TAG, "requestValidation: " + cid);

            // The default implementation is to return unsupported.
            executor.execute(() -> resultCodeCallback
                    .accept(DataServiceCallback.RESULT_ERROR_UNSUPPORTED));
        }

        /**
         * Notify the system that current data call list changed. Data service must invoke this
@@ -537,6 +585,17 @@ public abstract class DataService extends Service {
        }
    }

    private static final class ValidationRequest {
        public final int cid;
        public final Executor executor;
        public final IIntegerConsumer callback;
        ValidationRequest(int cid, Executor executor, IIntegerConsumer callback) {
            this.cid = cid;
            this.executor = executor;
            this.callback = callback;
        }
    }

    private class DataServiceHandler extends Handler {

        DataServiceHandler(Looper looper) {
@@ -679,6 +738,15 @@ public abstract class DataService extends Service {
                        loge("Failed to call onApnUnthrottled. " + e);
                    }
                    break;
                case DATA_SERVICE_REQUEST_VALIDATION:
                    if (serviceProvider == null) break;
                    ValidationRequest validationRequest = (ValidationRequest) message.obj;
                    serviceProvider.requestValidation(
                            validationRequest.cid,
                            validationRequest.executor,
                            FunctionalUtils
                                    .ignoreRemoteException(validationRequest.callback::accept));
                    break;
            }
        }
    }
@@ -691,6 +759,7 @@ public abstract class DataService extends Service {
        mHandlerThread.start();

        mHandler = new DataServiceHandler(mHandlerThread.getLooper());
        mHandlerExecutor = new HandlerExecutor(mHandler);
        log("Data service created");
    }

@@ -853,6 +922,18 @@ public abstract class DataService extends Service {
            mHandler.obtainMessage(DATA_SERVICE_REQUEST_UNREGISTER_APN_UNTHROTTLED,
                    slotIndex, 0, callback).sendToTarget();
        }

        @Override
        public void requestValidation(int slotIndex, int cid, IIntegerConsumer resultCodeCallback) {
            if (resultCodeCallback == null) {
                loge("requestValidation: resultCodeCallback is null");
                return;
            }
            ValidationRequest validationRequest =
                    new ValidationRequest(cid, mHandlerExecutor, resultCodeCallback);
            mHandler.obtainMessage(DATA_SERVICE_REQUEST_VALIDATION,
                    slotIndex, 0, validationRequest).sendToTarget();
        }
    }

    private void log(String s) {
Loading