diff options
| author | 2023-10-10 04:32:59 +0900 | |
|---|---|---|
| committer | 2023-11-11 00:04:10 +0900 | |
| commit | 978f45d3a14c4b22f900c0fb5810a1ffeb1ef157 (patch) | |
| tree | 1e24503931367428a3d75133bf79b0f251711fed | |
| parent | 06ab26fee3325433d7d1e9c99b17ab483e884026 (diff) | |
Add APIs for network validation feature.
Adds a feature API that allows the QualifiedNetworkService to request
network validation. Request network validation to the data network in
connected state corresponding to the given capability and add the
network validation state to PresiceDataConnctionState in response.
The DataService API that requests validation is also added. When a
network validation request is submitted, the request validation API
is returned imediately and the statuses or results are notified in
the DataCallResponse.
Bug: 299346675
Test: atest FrameworksTelephonyTests
Change-Id: Ifbf6034c0a032ee3551b72025c572d06b53fe810
8 files changed, 353 insertions, 11 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index fec797e64f8f..8d1cacafcbae 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -44638,10 +44638,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 { diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 6a5d07b95974..e2b343e2be2c 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -14777,6 +14777,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(); @@ -14813,6 +14814,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); @@ -14892,6 +14894,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); @@ -14953,6 +14956,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>); } diff --git a/telephony/java/android/telephony/PreciseDataConnectionState.java b/telephony/java/android/telephony/PreciseDataConnectionState.java index 7f1c14b877d2..b568f07135b8 100644 --- a/telephony/java/android/telephony/PreciseDataConnectionState.java +++ b/telephony/java/android/telephony/PreciseDataConnectionState.java @@ -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,11 +421,34 @@ 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 * * @hide @@ -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. * @@ -486,13 +580,27 @@ public final class PreciseDataConnectionState implements Parcelable { } /** + * 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. * * @return The {@link PreciseDataConnectionState} instance */ public PreciseDataConnectionState build() { return new PreciseDataConnectionState(mTransportType, mId, mState, mNetworkType, - mLinkProperties, mFailCause, mApnSetting, mDefaultQos); + mLinkProperties, mFailCause, mApnSetting, mDefaultQos, + mNetworkValidationStatus); } } } diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java index c7f0c5f753db..9dd83d1438e2 100644 --- a/telephony/java/android/telephony/data/DataCallResponse.java +++ b/telephony/java/android/telephony/data/DataCallResponse.java @@ -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. */ @@ -905,6 +930,20 @@ public final class DataCallResponse implements Parcelable { } /** + * 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. * * @return the DataCallResponse object. @@ -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); } } } diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java index d8b2cbebdf28..80e91a330185 100644 --- a/telephony/java/android/telephony/data/DataService.java +++ b/telephony/java/android/telephony/data/DataService.java @@ -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) { diff --git a/telephony/java/android/telephony/data/IDataService.aidl b/telephony/java/android/telephony/data/IDataService.aidl index 134694694a0e..15f88815ec6b 100644 --- a/telephony/java/android/telephony/data/IDataService.aidl +++ b/telephony/java/android/telephony/data/IDataService.aidl @@ -22,6 +22,8 @@ import android.telephony.data.IDataServiceCallback; import android.telephony.data.NetworkSliceInfo; import android.telephony.data.TrafficDescriptor; +import com.android.internal.telephony.IIntegerConsumer; + /** * {@hide} */ @@ -46,4 +48,5 @@ oneway interface IDataService void cancelHandover(int slotId, int cid, IDataServiceCallback callback); void registerForUnthrottleApn(int slotIndex, IDataServiceCallback callback); void unregisterForUnthrottleApn(int slotIndex, IDataServiceCallback callback); + void requestValidation(int slotId, int cid, IIntegerConsumer callback); } diff --git a/telephony/java/android/telephony/data/IQualifiedNetworksServiceCallback.aidl b/telephony/java/android/telephony/data/IQualifiedNetworksServiceCallback.aidl index 32ffdbc2121c..bdd212afd4b0 100644 --- a/telephony/java/android/telephony/data/IQualifiedNetworksServiceCallback.aidl +++ b/telephony/java/android/telephony/data/IQualifiedNetworksServiceCallback.aidl @@ -16,6 +16,8 @@ package android.telephony.data; +import com.android.internal.telephony.IIntegerConsumer; + /** * The qualified networks service call back interface * @hide @@ -23,4 +25,5 @@ package android.telephony.data; oneway interface IQualifiedNetworksServiceCallback { void onQualifiedNetworkTypesChanged(int apnTypes, in int[] qualifiedNetworkTypes); + void onNetworkValidationRequested(int networkCapability, IIntegerConsumer callback); } diff --git a/telephony/java/android/telephony/data/QualifiedNetworksService.java b/telephony/java/android/telephony/data/QualifiedNetworksService.java index 56f0f9f13772..c3ba09248298 100644 --- a/telephony/java/android/telephony/data/QualifiedNetworksService.java +++ b/telephony/java/android/telephony/data/QualifiedNetworksService.java @@ -16,6 +16,8 @@ package android.telephony.data; +import android.annotation.CallbackExecutor; +import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.SystemApi; import android.app.Service; @@ -29,13 +31,23 @@ import android.os.RemoteException; import android.telephony.AccessNetworkConstants; import android.telephony.AccessNetworkConstants.AccessNetworkType; import android.telephony.Annotation.ApnType; +import android.telephony.Annotation.NetCapability; +import android.telephony.PreciseDataConnectionState; 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.FeatureFlags; +import com.android.internal.telephony.flags.FeatureFlagsImpl; +import com.android.internal.telephony.flags.Flags; +import com.android.internal.util.FunctionalUtils; import com.android.telephony.Rlog; import java.util.List; +import java.util.Objects; +import java.util.concurrent.Executor; +import java.util.function.Consumer; /** * Base class of the qualified networks service, which is a vendor service providing up-to-date @@ -69,6 +81,10 @@ public abstract class QualifiedNetworksService extends Service { private static final int QNS_UPDATE_QUALIFIED_NETWORKS = 4; private static final int QNS_APN_THROTTLE_STATUS_CHANGED = 5; private static final int QNS_EMERGENCY_DATA_NETWORK_PREFERRED_TRANSPORT_CHANGED = 6; + private static final int QNS_REQUEST_NETWORK_VALIDATION = 7; + + /** Feature flags */ + private static final FeatureFlags sFeatureFlag = new FeatureFlagsImpl(); private final HandlerThread mHandlerThread; @@ -208,6 +224,72 @@ public abstract class QualifiedNetworksService extends Service { } /** + * Request network validation to the connected data network for given a network capability. + * + * <p>This network validation can only be performed when a data network is in connected + * state, and will not be triggered if the data network does not support network validation + * feature or network validation is not in connected state. + * + * <p>See {@link DataServiceCallback.ResultCode} for the type of response that indicates + * whether the request was successfully submitted or had an error. + * + * <p>If network validation is requested, monitor network validation status in {@link + * PreciseDataConnectionState#getNetworkValidationStatus()}. + * + * @param networkCapability A network capability. (Note that only APN-type capabilities are + * supported. + * @param executor executor The callback executor that responds whether the request has been + * successfully submitted or not. + * @param resultCodeCallback A callback to determine whether the request was successfully + * submitted or not. + */ + @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION) + public void requestNetworkValidation( + @NetCapability int networkCapability, + @NonNull @CallbackExecutor Executor executor, + @NonNull @DataServiceCallback.ResultCode Consumer<Integer> resultCodeCallback) { + Objects.requireNonNull(executor, "executor cannot be null"); + Objects.requireNonNull(resultCodeCallback, "resultCodeCallback cannot be null"); + + if (!sFeatureFlag.networkValidation()) { + loge("networkValidation feature is disabled"); + executor.execute( + () -> + resultCodeCallback.accept( + DataServiceCallback.RESULT_ERROR_UNSUPPORTED)); + return; + } + + IIntegerConsumer callback = new IIntegerConsumer.Stub() { + @Override + public void accept(int result) { + executor.execute(() -> resultCodeCallback.accept(result)); + } + }; + + // Move to the internal handler and process it. + mHandler.obtainMessage( + QNS_REQUEST_NETWORK_VALIDATION, + mSlotIndex, + 0, + new NetworkValidationRequestData(networkCapability, callback)) + .sendToTarget(); + } + + /** Process a network validation request on the internal handler. */ + private void onRequestNetworkValidation(NetworkValidationRequestData data) { + try { + log("onRequestNetworkValidation"); + // Callback to request a network validation. + mCallback.onNetworkValidationRequested(data.mNetworkCapability, data.mCallback); + } catch (RemoteException | NullPointerException e) { + loge("Failed to call onRequestNetworkValidation. " + e); + FunctionalUtils.ignoreRemoteException(data.mCallback::accept) + .accept(DataServiceCallback.RESULT_ERROR_UNSUPPORTED); + } + } + + /** * Called when the qualified networks provider is removed. The extended class should * implement this method to perform cleanup works. */ @@ -280,6 +362,10 @@ public abstract class QualifiedNetworksService extends Service { if (provider == null) break; provider.onUpdateQualifiedNetworkTypes(message.arg2, (int[]) message.obj); break; + + case QNS_REQUEST_NETWORK_VALIDATION: + if (provider == null) break; + provider.onRequestNetworkValidation((NetworkValidationRequestData) message.obj); } } } @@ -364,6 +450,17 @@ public abstract class QualifiedNetworksService extends Service { } } + private static final class NetworkValidationRequestData { + final @NetCapability int mNetworkCapability; + final IIntegerConsumer mCallback; + + private NetworkValidationRequestData(@NetCapability int networkCapability, + @NonNull IIntegerConsumer callback) { + mNetworkCapability = networkCapability; + mCallback = callback; + } + } + private void log(String s) { Rlog.d(TAG, s); } |