diff options
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); } |