diff options
65 files changed, 3516 insertions, 831 deletions
diff --git a/StubLibraries.bp b/StubLibraries.bp index 7060347922b9..aa39824e8614 100644 --- a/StubLibraries.bp +++ b/StubLibraries.bp @@ -177,31 +177,6 @@ module_libs = " " + "\\) " droidstubs { - name: "system-api-stubs-docs", - defaults: ["metalava-full-api-stubs-default"], - arg_files: [ - "core/res/AndroidManifest.xml", - ], - args: metalava_framework_docs_args + priv_apps, - check_api: { - current: { - api_file: "api/system-current.txt", - removed_api_file: "api/system-removed.txt", - }, - last_released: { - api_file: ":android.api.system.latest", - removed_api_file: ":removed.api.system.latest", - baseline_file: ":system-api-incompatibilities-with-last-released" - }, - api_lint: { - enabled: true, - new_since: ":android.api.system.latest", - baseline_file: "api/system-lint-baseline.txt", - }, - }, -} - -droidstubs { name: "system-api-stubs-docs-non-updatable", defaults: ["metalava-non-updatable-api-stubs-default"], arg_files: ["core/res/AndroidManifest.xml"], diff --git a/apex/permission/Android.bp b/apex/permission/Android.bp index 71a52bb216ea..e30df05b2340 100644 --- a/apex/permission/Android.bp +++ b/apex/permission/Android.bp @@ -21,7 +21,7 @@ apex { apex_defaults { name: "com.android.permission-defaults", updatable: true, - min_sdk_version: "R", + min_sdk_version: "30", key: "com.android.permission.key", certificate: ":com.android.permission.certificate", java_libs: [ diff --git a/api/current.txt b/api/current.txt index 010c896235e3..69f76451a5c4 100644 --- a/api/current.txt +++ b/api/current.txt @@ -45851,6 +45851,8 @@ package android.telecom { field public static final String EXTRA_ANSWERING_DROPS_FG_CALL = "android.telecom.extra.ANSWERING_DROPS_FG_CALL"; field public static final String EXTRA_ANSWERING_DROPS_FG_CALL_APP_NAME = "android.telecom.extra.ANSWERING_DROPS_FG_CALL_APP_NAME"; field public static final String EXTRA_AUDIO_CODEC = "android.telecom.extra.AUDIO_CODEC"; + field public static final String EXTRA_AUDIO_CODEC_BANDWIDTH_KHZ = "android.telecom.extra.AUDIO_CODEC_BANDWIDTH_KHZ"; + field public static final String EXTRA_AUDIO_CODEC_BITRATE_KBPS = "android.telecom.extra.AUDIO_CODEC_BITRATE_KBPS"; field public static final String EXTRA_CALL_SUBJECT = "android.telecom.extra.CALL_SUBJECT"; field public static final String EXTRA_CHILD_ADDRESS = "android.telecom.extra.CHILD_ADDRESS"; field public static final String EXTRA_IS_RTT_AUDIO_PRESENT = "android.telecom.extra.IS_RTT_AUDIO_PRESENT"; @@ -46297,6 +46299,7 @@ package android.telecom { method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getVoiceMailNumber(android.telecom.PhoneAccountHandle); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean handleMmi(String); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean handleMmi(String, android.telecom.PhoneAccountHandle); + method public boolean hasCompanionInCallServiceAccess(); method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isInCall(); method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isInManagedCall(); method public boolean isIncomingCallPermitted(android.telecom.PhoneAccountHandle); diff --git a/api/system-current.txt b/api/system-current.txt index abe95aebf8b2..dcffa06ead8f 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -10592,6 +10592,17 @@ package android.telephony { field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CallQuality> CREATOR; } + public final class CarrierBandwidth implements android.os.Parcelable { + ctor public CarrierBandwidth(int, int, int, int); + method public int describeContents(); + method public int getPrimaryDownlinkCapacityKbps(); + method public int getPrimaryUplinkCapacityKbps(); + method public int getSecondaryDownlinkCapacityKbps(); + method public int getSecondaryUplinkCapacityKbps(); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CarrierBandwidth> CREATOR; + field public static final int INVALID = -1; // 0xffffffff + } + public class CarrierConfigManager { method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getDefaultCarrierServicePackageName(); method @NonNull public static android.os.PersistableBundle getDefaultConfig(); @@ -11208,6 +11219,7 @@ package android.telephony { method @Nullable @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public android.content.ComponentName getAndUpdateDefaultRespondViaMessageApplication(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getCallForwarding(int, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CallForwardingInfoCallback); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getCallWaitingStatus(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); + method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.CarrierBandwidth getCarrierBandwidth(); method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int); method public java.util.List<java.lang.String> getCarrierPackageNamesForIntent(android.content.Intent); method public java.util.List<java.lang.String> getCarrierPackageNamesForIntentAndPhone(android.content.Intent, int); @@ -11807,7 +11819,6 @@ package android.telephony.ims { method public int getEmergencyServiceCategories(); method @NonNull public java.util.List<java.lang.String> getEmergencyUrns(); method public android.telephony.ims.ImsStreamMediaProfile getMediaProfile(); - method @NonNull public java.util.Set<android.telephony.ims.RtpHeaderExtensionType> getOfferedRtpHeaderExtensionTypes(); method @NonNull public android.os.Bundle getProprietaryCallExtras(); method public int getRestrictCause(); method public int getServiceType(); @@ -11829,7 +11840,6 @@ package android.telephony.ims { method public void setEmergencyServiceCategories(int); method public void setEmergencyUrns(@NonNull java.util.List<java.lang.String>); method public void setHasKnownUserIntentEmergency(boolean); - method public void setOfferedRtpHeaderExtensionTypes(@NonNull java.util.Set<android.telephony.ims.RtpHeaderExtensionType>); method public void updateCallExtras(android.telephony.ims.ImsCallProfile); method public void updateCallType(android.telephony.ims.ImsCallProfile); method public void updateMediaProfile(android.telephony.ims.ImsCallProfile); @@ -12338,6 +12348,7 @@ package android.telephony.ims.feature { public class MmTelFeature extends android.telephony.ims.feature.ImsFeature { ctor public MmTelFeature(); method public void changeEnabledCapabilities(@NonNull android.telephony.ims.feature.CapabilityChangeRequest, @NonNull android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy); + method public void changeOfferedRtpHeaderExtensionTypes(@NonNull java.util.Set<android.telephony.ims.RtpHeaderExtensionType>); method @Nullable public android.telephony.ims.ImsCallProfile createCallProfile(int, int); method @Nullable public android.telephony.ims.stub.ImsCallSessionImplBase createCallSession(@NonNull android.telephony.ims.ImsCallProfile); method @NonNull public android.telephony.ims.stub.ImsEcbmImplBase getEcbm(); diff --git a/core/api/current.txt b/core/api/current.txt index 75b34cd096e7..055f909077f7 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -44019,6 +44019,8 @@ package android.telecom { field public static final String EXTRA_ANSWERING_DROPS_FG_CALL = "android.telecom.extra.ANSWERING_DROPS_FG_CALL"; field public static final String EXTRA_ANSWERING_DROPS_FG_CALL_APP_NAME = "android.telecom.extra.ANSWERING_DROPS_FG_CALL_APP_NAME"; field public static final String EXTRA_AUDIO_CODEC = "android.telecom.extra.AUDIO_CODEC"; + field public static final String EXTRA_AUDIO_CODEC_BANDWIDTH_KHZ = "android.telecom.extra.AUDIO_CODEC_BANDWIDTH_KHZ"; + field public static final String EXTRA_AUDIO_CODEC_BITRATE_KBPS = "android.telecom.extra.AUDIO_CODEC_BITRATE_KBPS"; field public static final String EXTRA_CALL_SUBJECT = "android.telecom.extra.CALL_SUBJECT"; field public static final String EXTRA_CHILD_ADDRESS = "android.telecom.extra.CHILD_ADDRESS"; field public static final String EXTRA_IS_RTT_AUDIO_PRESENT = "android.telecom.extra.IS_RTT_AUDIO_PRESENT"; @@ -44465,6 +44467,7 @@ package android.telecom { method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getVoiceMailNumber(android.telecom.PhoneAccountHandle); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean handleMmi(String); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean handleMmi(String, android.telecom.PhoneAccountHandle); + method public boolean hasCompanionInCallServiceAccess(); method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isInCall(); method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isInManagedCall(); method public boolean isIncomingCallPermitted(android.telecom.PhoneAccountHandle); diff --git a/core/api/system-current.txt b/core/api/system-current.txt index a795a53ac3b5..18d10641d0cd 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -9474,6 +9474,17 @@ package android.telephony { field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CallQuality> CREATOR; } + public final class CarrierBandwidth implements android.os.Parcelable { + ctor public CarrierBandwidth(int, int, int, int); + method public int describeContents(); + method public int getPrimaryDownlinkCapacityKbps(); + method public int getPrimaryUplinkCapacityKbps(); + method public int getSecondaryDownlinkCapacityKbps(); + method public int getSecondaryUplinkCapacityKbps(); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CarrierBandwidth> CREATOR; + field public static final int INVALID = -1; // 0xffffffff + } + public class CarrierConfigManager { method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getDefaultCarrierServicePackageName(); method @NonNull public static android.os.PersistableBundle getDefaultConfig(); @@ -10090,6 +10101,7 @@ package android.telephony { method @Nullable @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public android.content.ComponentName getAndUpdateDefaultRespondViaMessageApplication(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getCallForwarding(int, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CallForwardingInfoCallback); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getCallWaitingStatus(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); + method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.CarrierBandwidth getCarrierBandwidth(); method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int); method public java.util.List<java.lang.String> getCarrierPackageNamesForIntent(android.content.Intent); method public java.util.List<java.lang.String> getCarrierPackageNamesForIntentAndPhone(android.content.Intent, int); @@ -10689,7 +10701,6 @@ package android.telephony.ims { method public int getEmergencyServiceCategories(); method @NonNull public java.util.List<java.lang.String> getEmergencyUrns(); method public android.telephony.ims.ImsStreamMediaProfile getMediaProfile(); - method @NonNull public java.util.Set<android.telephony.ims.RtpHeaderExtensionType> getOfferedRtpHeaderExtensionTypes(); method @NonNull public android.os.Bundle getProprietaryCallExtras(); method public int getRestrictCause(); method public int getServiceType(); @@ -10711,7 +10722,6 @@ package android.telephony.ims { method public void setEmergencyServiceCategories(int); method public void setEmergencyUrns(@NonNull java.util.List<java.lang.String>); method public void setHasKnownUserIntentEmergency(boolean); - method public void setOfferedRtpHeaderExtensionTypes(@NonNull java.util.Set<android.telephony.ims.RtpHeaderExtensionType>); method public void updateCallExtras(android.telephony.ims.ImsCallProfile); method public void updateCallType(android.telephony.ims.ImsCallProfile); method public void updateMediaProfile(android.telephony.ims.ImsCallProfile); @@ -11220,6 +11230,7 @@ package android.telephony.ims.feature { public class MmTelFeature extends android.telephony.ims.feature.ImsFeature { ctor public MmTelFeature(); method public void changeEnabledCapabilities(@NonNull android.telephony.ims.feature.CapabilityChangeRequest, @NonNull android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy); + method public void changeOfferedRtpHeaderExtensionTypes(@NonNull java.util.Set<android.telephony.ims.RtpHeaderExtensionType>); method @Nullable public android.telephony.ims.ImsCallProfile createCallProfile(int, int); method @Nullable public android.telephony.ims.stub.ImsCallSessionImplBase createCallSession(@NonNull android.telephony.ims.ImsCallProfile); method @NonNull public android.telephony.ims.stub.ImsEcbmImplBase getEcbm(); diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index dbddc2217273..66482597311a 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -40,7 +40,7 @@ import android.app.PropertyInvalidatedCache; import android.app.admin.DevicePolicyManager; import android.app.usage.StorageStatsManager; import android.compat.annotation.ChangeId; -import android.compat.annotation.EnabledAfter; +import android.compat.annotation.EnabledSince; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; @@ -3682,7 +3682,7 @@ public abstract class PackageManager { * @hide */ @ChangeId - @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q) + @EnabledSince(targetSdkVersion = Build.VERSION_CODES.R) public static final long FILTER_APPLICATION_QUERY = 135549675L; /** {@hide} */ diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java index a985e934ed3c..737683050528 100644 --- a/core/java/android/net/TrafficStats.java +++ b/core/java/android/net/TrafficStats.java @@ -976,11 +976,17 @@ public class TrafficStats { } } - // NOTE: keep these in sync with android_net_TrafficStats.cpp - private static final int TYPE_RX_BYTES = 0; - private static final int TYPE_RX_PACKETS = 1; - private static final int TYPE_TX_BYTES = 2; - private static final int TYPE_TX_PACKETS = 3; - private static final int TYPE_TCP_RX_PACKETS = 4; - private static final int TYPE_TCP_TX_PACKETS = 5; + // NOTE: keep these in sync with {@code com_android_server_net_NetworkStatsService.cpp}. + /** {@hide} */ + public static final int TYPE_RX_BYTES = 0; + /** {@hide} */ + public static final int TYPE_RX_PACKETS = 1; + /** {@hide} */ + public static final int TYPE_TX_BYTES = 2; + /** {@hide} */ + public static final int TYPE_TX_PACKETS = 3; + /** {@hide} */ + public static final int TYPE_TCP_RX_PACKETS = 4; + /** {@hide} */ + public static final int TYPE_TCP_TX_PACKETS = 5; } diff --git a/core/java/android/uwb/RangingParams.java b/core/java/android/uwb/RangingParams.java deleted file mode 100644 index f23d9ed0dd3a..000000000000 --- a/core/java/android/uwb/RangingParams.java +++ /dev/null @@ -1,477 +0,0 @@ -/* - * Copyright 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.uwb; - -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.os.Parcel; -import android.os.Parcelable; -import android.os.PersistableBundle; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.time.Duration; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Objects; -import java.util.Set; - -/** - * An object used when requesting to open a new {@link RangingSession}. - * <p>Use {@link RangingParams.Builder} to create an instance of this class. - * - * @hide - */ -public final class RangingParams implements Parcelable { - private final boolean mIsInitiator; - private final boolean mIsController; - private final Duration mSamplePeriod; - private final UwbAddress mLocalDeviceAddress; - private final List<UwbAddress> mRemoteDeviceAddresses; - private final int mChannelNumber; - private final int mTransmitPreambleCodeIndex; - private final int mReceivePreambleCodeIndex; - private final int mStsPhyPacketType; - private final PersistableBundle mSpecificationParameters; - - private RangingParams(boolean isInitiator, boolean isController, - @NonNull Duration samplingPeriod, @NonNull UwbAddress localDeviceAddress, - @NonNull List<UwbAddress> remoteDeviceAddresses, int channelNumber, - int transmitPreambleCodeIndex, int receivePreambleCodeIndex, - @StsPhyPacketType int stsPhyPacketType, - @NonNull PersistableBundle specificationParameters) { - mIsInitiator = isInitiator; - mIsController = isController; - mSamplePeriod = samplingPeriod; - mLocalDeviceAddress = localDeviceAddress; - mRemoteDeviceAddresses = remoteDeviceAddresses; - mChannelNumber = channelNumber; - mTransmitPreambleCodeIndex = transmitPreambleCodeIndex; - mReceivePreambleCodeIndex = receivePreambleCodeIndex; - mStsPhyPacketType = stsPhyPacketType; - mSpecificationParameters = specificationParameters; - } - - /** - * Get if the local device is the initiator - * - * @return true if the device is the initiator - */ - public boolean isInitiator() { - return mIsInitiator; - } - - /** - * Get if the local device is the controller - * - * @return true if the device is the controller - */ - public boolean isController() { - return mIsController; - } - - /** - * The desired amount of time between two adjacent samples of measurement - * - * @return the ranging sample period - */ - @NonNull - public Duration getSamplingPeriod() { - return mSamplePeriod; - } - - /** - * Local device's {@link UwbAddress} - * - * <p>Simultaneous {@link RangingSession}s on the same device can have different results for - * {@link #getLocalDeviceAddress()}. - * - * @return the local device's {@link UwbAddress} - */ - @NonNull - public UwbAddress getLocalDeviceAddress() { - return mLocalDeviceAddress; - } - - /** - * Gets a list of all remote device's {@link UwbAddress} - * - * @return a {@link List} of {@link UwbAddress} representing the remote devices - */ - @NonNull - public List<UwbAddress> getRemoteDeviceAddresses() { - return mRemoteDeviceAddresses; - } - - /** - * Channel number used between this device pair as defined by 802.15.4z - * - * Range: -1, 0-15 - * - * @return the channel to use - */ - public int getChannelNumber() { - return mChannelNumber; - } - - /** - * Preamble index used between this device pair as defined by 802.15.4z - * - * Range: 0, 0-32 - * - * @return the preamble index to use for transmitting - */ - public int getTxPreambleIndex() { - return mTransmitPreambleCodeIndex; - } - - /** - * preamble index used between this device pair as defined by 802.15.4z - * - * Range: 0, 13-16, 21-32 - * - * @return the preamble index to use for receiving - */ - public int getRxPreambleIndex() { - return mReceivePreambleCodeIndex; - } - - @Retention(RetentionPolicy.SOURCE) - @IntDef(value = { - STS_PHY_PACKET_TYPE_SP0, - STS_PHY_PACKET_TYPE_SP1, - STS_PHY_PACKET_TYPE_SP2, - STS_PHY_PACKET_TYPE_SP3}) - public @interface StsPhyPacketType {} - - /** - * PHY packet type SP0 when STS is used as defined by 802.15.4z - */ - public static final int STS_PHY_PACKET_TYPE_SP0 = 0; - - /** - * PHY packet type SP1 when STS is used as defined by 802.15.4z - */ - public static final int STS_PHY_PACKET_TYPE_SP1 = 1; - - /** - * PHY packet type SP2 when STS is used as defined by 802.15.4z - */ - public static final int STS_PHY_PACKET_TYPE_SP2 = 2; - - /** - * PHY packet type SP3 when STS is used as defined by 802.15.4z - */ - public static final int STS_PHY_PACKET_TYPE_SP3 = 3; - - /** - * Get the type of PHY packet when STS is used as defined by 802.15.4z - * - * @return the {@link StsPhyPacketType} to use - */ - @StsPhyPacketType - public int getStsPhyPacketType() { - return mStsPhyPacketType; - } - - /** - * Parameters for a specific UWB protocol constructed using a support library. - * - * <p>Android reserves the '^android.*' namespace - * - * @return a {@link PersistableBundle} copy of protocol specific parameters - */ - public @Nullable PersistableBundle getSpecificationParameters() { - return new PersistableBundle(mSpecificationParameters); - } - - /** - * @hide - */ - @Override - public boolean equals(@Nullable Object obj) { - if (this == obj) { - return true; - } - - if (obj instanceof RangingParams) { - RangingParams other = (RangingParams) obj; - - return mIsInitiator == other.mIsInitiator - && mIsController == other.mIsController - && mSamplePeriod.equals(other.mSamplePeriod) - && mLocalDeviceAddress.equals(other.mLocalDeviceAddress) - && mRemoteDeviceAddresses.equals(other.mRemoteDeviceAddresses) - && mChannelNumber == other.mChannelNumber - && mTransmitPreambleCodeIndex == other.mTransmitPreambleCodeIndex - && mReceivePreambleCodeIndex == other.mReceivePreambleCodeIndex - && mStsPhyPacketType == other.mStsPhyPacketType - && mSpecificationParameters.size() == other.mSpecificationParameters.size() - && mSpecificationParameters.kindofEquals(other.mSpecificationParameters); - } - return false; - } - - /** - * @hide - */ - @Override - public int hashCode() { - return Objects.hash(mIsInitiator, mIsController, mSamplePeriod, mLocalDeviceAddress, - mRemoteDeviceAddresses, mChannelNumber, mTransmitPreambleCodeIndex, - mReceivePreambleCodeIndex, mStsPhyPacketType, mSpecificationParameters); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeBoolean(mIsInitiator); - dest.writeBoolean(mIsController); - dest.writeLong(mSamplePeriod.getSeconds()); - dest.writeInt(mSamplePeriod.getNano()); - dest.writeParcelable(mLocalDeviceAddress, flags); - - UwbAddress[] remoteAddresses = new UwbAddress[mRemoteDeviceAddresses.size()]; - mRemoteDeviceAddresses.toArray(remoteAddresses); - dest.writeParcelableArray(remoteAddresses, flags); - - dest.writeInt(mChannelNumber); - dest.writeInt(mTransmitPreambleCodeIndex); - dest.writeInt(mReceivePreambleCodeIndex); - dest.writeInt(mStsPhyPacketType); - dest.writePersistableBundle(mSpecificationParameters); - } - - public static final @android.annotation.NonNull Creator<RangingParams> CREATOR = - new Creator<RangingParams>() { - @Override - public RangingParams createFromParcel(Parcel in) { - Builder builder = new Builder(); - builder.setIsInitiator(in.readBoolean()); - builder.setIsController(in.readBoolean()); - builder.setSamplePeriod(Duration.ofSeconds(in.readLong(), in.readInt())); - builder.setLocalDeviceAddress( - in.readParcelable(UwbAddress.class.getClassLoader())); - - UwbAddress[] remoteAddresses = - in.readParcelableArray(null, UwbAddress.class); - for (UwbAddress remoteAddress : remoteAddresses) { - builder.addRemoteDeviceAddress(remoteAddress); - } - - builder.setChannelNumber(in.readInt()); - builder.setTransmitPreambleCodeIndex(in.readInt()); - builder.setReceivePreambleCodeIndex(in.readInt()); - builder.setStsPhPacketType(in.readInt()); - builder.setSpecificationParameters(in.readPersistableBundle()); - - return builder.build(); - } - - @Override - public RangingParams[] newArray(int size) { - return new RangingParams[size]; - } - }; - - /** - * Builder class for {@link RangingParams}. - */ - public static final class Builder { - private boolean mIsInitiator = false; - private boolean mIsController = false; - private Duration mSamplePeriod = null; - private UwbAddress mLocalDeviceAddress = null; - private List<UwbAddress> mRemoteDeviceAddresses = new ArrayList<>(); - private int mChannelNumber = 0; - private int mTransmitPreambleCodeIndex = 0; - private int mReceivePreambleCodeIndex = 0; - private int mStsPhyPacketType = STS_PHY_PACKET_TYPE_SP0; - private PersistableBundle mSpecificationParameters = new PersistableBundle(); - - /** - * Set whether the device is the initiator or responder as defined by IEEE 802.15.4z - * - * @param isInitiator whether the device is the initiator (true) or responder (false) - */ - public Builder setIsInitiator(boolean isInitiator) { - mIsInitiator = isInitiator; - return this; - } - - /** - * Set whether the local device is the controller or controlee as defined by IEEE 802.15.4z - * - * @param isController whether the device is the controller (true) or controlee (false) - */ - public Builder setIsController(boolean isController) { - mIsController = isController; - return this; - } - - /** - * Set the time between ranging samples - * - * @param samplePeriod the time between ranging samples - */ - public Builder setSamplePeriod(@NonNull Duration samplePeriod) { - mSamplePeriod = samplePeriod; - return this; - } - - /** - * Set the local device address - * - * @param localDeviceAddress the local device's address for the {@link RangingSession} - */ - public Builder setLocalDeviceAddress(@NonNull UwbAddress localDeviceAddress) { - mLocalDeviceAddress = localDeviceAddress; - return this; - } - - /** - * Add a remote device's address to the ranging session - * - * @param remoteDeviceAddress a remote device's address for the {@link RangingSession} - * @throws IllegalArgumentException if {@code remoteDeviceAddress} is already present. - */ - public Builder addRemoteDeviceAddress(@NonNull UwbAddress remoteDeviceAddress) { - if (mRemoteDeviceAddresses.contains(remoteDeviceAddress)) { - throw new IllegalArgumentException( - "Remote device address already added: " + remoteDeviceAddress.toString()); - } - mRemoteDeviceAddresses.add(remoteDeviceAddress); - return this; - } - - /** - * Set the IEEE 802.15.4z channel to use for the {@link RangingSession} - * <p>Valid values are in the range [-1, 15] - * - * @param channelNumber the channel to use for the {@link RangingSession} - * @throws IllegalArgumentException if {@code channelNumber} is invalid. - */ - public Builder setChannelNumber(int channelNumber) { - if (channelNumber < -1 || channelNumber > 15) { - throw new IllegalArgumentException("Invalid channel number"); - } - mChannelNumber = channelNumber; - return this; - } - - private static final Set<Integer> VALID_TX_PREAMBLE_CODES = new HashSet<Integer>( - Arrays.asList(0, 13, 14, 15, 16, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32)); - - /** - * Set the IEEE 802.15.4z preamble code index to use when transmitting - * - * <p>Valid values are in the ranges: [0], [13-16], [21-32] - * - * @param transmitPreambleCodeIndex preamble code index to use for transmitting - * @throws IllegalArgumentException if {@code transmitPreambleCodeIndex} is invalid. - */ - public Builder setTransmitPreambleCodeIndex(int transmitPreambleCodeIndex) { - if (!VALID_TX_PREAMBLE_CODES.contains(transmitPreambleCodeIndex)) { - throw new IllegalArgumentException( - "Invalid transmit preamble: " + transmitPreambleCodeIndex); - } - mTransmitPreambleCodeIndex = transmitPreambleCodeIndex; - return this; - } - - private static final Set<Integer> VALID_RX_PREAMBLE_CODES = new HashSet<Integer>( - Arrays.asList(0, 16, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32)); - - /** - * Set the IEEE 802.15.4z preamble code index to use when receiving - * - * Valid values are in the ranges: [0], [16-32] - * - * @param receivePreambleCodeIndex preamble code index to use for receiving - * @throws IllegalArgumentException if {@code receivePreambleCodeIndex} is invalid. - */ - public Builder setReceivePreambleCodeIndex(int receivePreambleCodeIndex) { - if (!VALID_RX_PREAMBLE_CODES.contains(receivePreambleCodeIndex)) { - throw new IllegalArgumentException( - "Invalid receive preamble: " + receivePreambleCodeIndex); - } - mReceivePreambleCodeIndex = receivePreambleCodeIndex; - return this; - } - - /** - * Set the IEEE 802.15.4z PHY packet type when STS is used - * - * @param stsPhyPacketType PHY packet type when STS is used - * @throws IllegalArgumentException if {@code stsPhyPacketType} is invalid. - */ - public Builder setStsPhPacketType(@StsPhyPacketType int stsPhyPacketType) { - if (stsPhyPacketType != STS_PHY_PACKET_TYPE_SP0 - && stsPhyPacketType != STS_PHY_PACKET_TYPE_SP1 - && stsPhyPacketType != STS_PHY_PACKET_TYPE_SP2 - && stsPhyPacketType != STS_PHY_PACKET_TYPE_SP3) { - throw new IllegalArgumentException("unknown StsPhyPacketType: " + stsPhyPacketType); - } - - mStsPhyPacketType = stsPhyPacketType; - return this; - } - - /** - * Set the specification parameters - * - * <p>Creates a copy of the parameters - * - * @param parameters specification parameters built from support library - */ - public Builder setSpecificationParameters(@NonNull PersistableBundle parameters) { - mSpecificationParameters = new PersistableBundle(parameters); - return this; - } - - /** - * Build the {@link RangingParams} object. - * - * @throws IllegalStateException if required parameters are missing - */ - public RangingParams build() { - if (mSamplePeriod == null) { - throw new IllegalStateException("No sample period provided"); - } - - if (mLocalDeviceAddress == null) { - throw new IllegalStateException("Local device address not provided"); - } - - if (mRemoteDeviceAddresses.size() == 0) { - throw new IllegalStateException("No remote device address(es) provided"); - } - - return new RangingParams(mIsInitiator, mIsController, mSamplePeriod, - mLocalDeviceAddress, mRemoteDeviceAddresses, mChannelNumber, - mTransmitPreambleCodeIndex, mReceivePreambleCodeIndex, mStsPhyPacketType, - mSpecificationParameters); - } - } -} diff --git a/core/java/android/uwb/RangingSession.java b/core/java/android/uwb/RangingSession.java index f4033fe0d29e..863926924aad 100644 --- a/core/java/android/uwb/RangingSession.java +++ b/core/java/android/uwb/RangingSession.java @@ -30,7 +30,7 @@ import java.util.concurrent.Executor; * {@link RangingSession}. * * <p>To get an instance of {@link RangingSession}, first use - * {@link UwbManager#openRangingSession(RangingParams, Executor, Callback)} to request to open a + * {@link UwbManager#openRangingSession(PersistableBundle, Executor, Callback)} to request to open a * session. Once the session is opened, a {@link RangingSession} object is provided through * {@link RangingSession.Callback#onOpenSuccess(RangingSession, PersistableBundle)}. If opening a * session fails, the failure is reported through {@link RangingSession.Callback#onClosed(int)} with @@ -44,7 +44,7 @@ public final class RangingSession implements AutoCloseable { */ public interface Callback { /** - * Invoked when {@link UwbManager#openRangingSession(RangingParams, Executor, Callback)} + * Invoked when {@link UwbManager#openRangingSession(PersistableBundle, Executor, Callback)} * is successful * * @param session the newly opened {@link RangingSession} @@ -77,7 +77,7 @@ public final class RangingSession implements AutoCloseable { /** * Indicates that the session failed to open due to erroneous parameters passed - * to {@link UwbManager#openRangingSession(RangingParams, Executor, Callback)} + * to {@link UwbManager#openRangingSession(PersistableBundle, Executor, Callback)} */ int CLOSE_REASON_LOCAL_BAD_PARAMETERS = 2; @@ -137,8 +137,8 @@ public final class RangingSession implements AutoCloseable { * will still be invoked. * * <p>{@link Callback#onClosed(int)} will be invoked using the same callback - * object given to {@link UwbManager#openRangingSession(RangingParams, Executor, Callback)} when - * the {@link RangingSession} was opened. The callback will be invoked after each call to + * object given to {@link UwbManager#openRangingSession(PersistableBundle, Executor, Callback)} + * when the {@link RangingSession} was opened. The callback will be invoked after each call to * {@link #close()}, even if the {@link RangingSession} is already closed. */ @Override diff --git a/core/java/android/uwb/UwbManager.java b/core/java/android/uwb/UwbManager.java index 6bf7d6ffa5d9..2f1e2ded26ac 100644 --- a/core/java/android/uwb/UwbManager.java +++ b/core/java/android/uwb/UwbManager.java @@ -279,7 +279,10 @@ public final class UwbManager { * <p>An open {@link RangingSession} will be automatically closed if client application process * dies. * - * @param params {@link RangingParams} used to initialize this {@link RangingSession} + * <p>A UWB support library must be used in order to construct the {@code parameter} + * {@link PersistableBundle}. + * + * @param parameters the parameters that define the ranging session * @param executor {@link Executor} to run callbacks * @param callbacks {@link RangingSession.Callback} to associate with the * {@link RangingSession} that is being opened. @@ -290,8 +293,9 @@ public final class UwbManager { * {@link RangingSession.Callback#onOpenSuccess}. */ @NonNull - public AutoCloseable openRangingSession(@NonNull RangingParams params, - @NonNull Executor executor, @NonNull RangingSession.Callback callbacks) { + public AutoCloseable openRangingSession(@NonNull PersistableBundle parameters, + @NonNull Executor executor, + @NonNull RangingSession.Callback callbacks) { throw new UnsupportedOperationException(); } } diff --git a/core/tests/uwbtests/src/android/uwb/RangingParamsTest.java b/core/tests/uwbtests/src/android/uwb/RangingParamsTest.java deleted file mode 100644 index 8095c995b8a9..000000000000 --- a/core/tests/uwbtests/src/android/uwb/RangingParamsTest.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.uwb; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import android.os.Parcel; -import android.os.PersistableBundle; - -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.filters.SmallTest; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.time.Duration; - -/** - * Test of {@link RangingParams}. - */ -@SmallTest -@RunWith(AndroidJUnit4.class) -public class RangingParamsTest { - - @Test - public void testParams_Build() { - UwbAddress local = UwbAddress.fromBytes(new byte[] {(byte) 0xA0, (byte) 0x57}); - UwbAddress remote = UwbAddress.fromBytes(new byte[] {(byte) 0x4D, (byte) 0x8C}); - int channel = 9; - int rxPreamble = 16; - int txPreamble = 21; - boolean isController = true; - boolean isInitiator = false; - @RangingParams.StsPhyPacketType int stsPhyType = RangingParams.STS_PHY_PACKET_TYPE_SP2; - Duration samplePeriod = Duration.ofSeconds(1, 234); - PersistableBundle specParams = new PersistableBundle(); - specParams.putString("protocol", "some_protocol"); - - RangingParams params = new RangingParams.Builder() - .setChannelNumber(channel) - .setReceivePreambleCodeIndex(rxPreamble) - .setTransmitPreambleCodeIndex(txPreamble) - .setLocalDeviceAddress(local) - .addRemoteDeviceAddress(remote) - .setIsController(isController) - .setIsInitiator(isInitiator) - .setSamplePeriod(samplePeriod) - .setStsPhPacketType(stsPhyType) - .setSpecificationParameters(specParams) - .build(); - - assertEquals(params.getLocalDeviceAddress(), local); - assertEquals(params.getRemoteDeviceAddresses().size(), 1); - assertEquals(params.getRemoteDeviceAddresses().get(0), remote); - assertEquals(params.getChannelNumber(), channel); - assertEquals(params.isController(), isController); - assertEquals(params.isInitiator(), isInitiator); - assertEquals(params.getRxPreambleIndex(), rxPreamble); - assertEquals(params.getTxPreambleIndex(), txPreamble); - assertEquals(params.getStsPhyPacketType(), stsPhyType); - assertEquals(params.getSamplingPeriod(), samplePeriod); - assertTrue(params.getSpecificationParameters().kindofEquals(specParams)); - } - - @Test - public void testParcel() { - Parcel parcel = Parcel.obtain(); - RangingParams params = new RangingParams.Builder() - .setChannelNumber(9) - .setReceivePreambleCodeIndex(16) - .setTransmitPreambleCodeIndex(21) - .setLocalDeviceAddress(UwbTestUtils.getUwbAddress(false)) - .addRemoteDeviceAddress(UwbTestUtils.getUwbAddress(true)) - .setIsController(false) - .setIsInitiator(true) - .setSamplePeriod(Duration.ofSeconds(2)) - .setStsPhPacketType(RangingParams.STS_PHY_PACKET_TYPE_SP1) - .build(); - params.writeToParcel(parcel, 0); - parcel.setDataPosition(0); - RangingParams fromParcel = RangingParams.CREATOR.createFromParcel(parcel); - assertEquals(params, fromParcel); - } -} diff --git a/keystore/java/android/security/keystore/ArrayUtils.java b/keystore/java/android/security/keystore/ArrayUtils.java index f519c7cdd3d2..c8c1de4a5e83 100644 --- a/keystore/java/android/security/keystore/ArrayUtils.java +++ b/keystore/java/android/security/keystore/ArrayUtils.java @@ -18,6 +18,8 @@ package android.security.keystore; import libcore.util.EmptyArray; +import java.util.function.Consumer; + /** * @hide */ @@ -107,4 +109,16 @@ public abstract class ArrayUtils { return result; } } + + /** + * Runs {@code consumer.accept()} for each element of {@code array}. + * @param array + * @param consumer + * @hide + */ + public static void forEach(int[] array, Consumer<Integer> consumer) { + for (int i : array) { + consumer.accept(i); + } + } } diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java index 12c24d418611..81a6641de8a4 100644 --- a/services/core/java/com/android/server/net/NetworkStatsService.java +++ b/services/core/java/com/android/server/net/NetworkStatsService.java @@ -1084,12 +1084,10 @@ public class NetworkStatsService extends INetworkStatsService.Stub { return nativeIfaceStats; } else { // When tethering offload is in use, nativeIfaceStats does not contain usage from - // offload, add it back here. - // When tethering offload is not in use, nativeIfaceStats contains tethering usage. - // this does not cause double-counting of tethering traffic, because - // NetdTetheringStatsProvider returns zero NetworkStats - // when called with STATS_PER_IFACE. - return nativeIfaceStats + getTetherStats(iface, type); + // offload, add it back here. Note that the included statistics might be stale + // since polling newest stats from hardware might impact system health and not + // suitable for TrafficStats API use cases. + return nativeIfaceStats + getProviderIfaceStats(iface, type); } } @@ -1100,39 +1098,28 @@ public class NetworkStatsService extends INetworkStatsService.Stub { return nativeTotalStats; } else { // Refer to comment in getIfaceStats - return nativeTotalStats + getTetherStats(IFACE_ALL, type); + return nativeTotalStats + getProviderIfaceStats(IFACE_ALL, type); } } - private long getTetherStats(String iface, int type) { - final NetworkStats tetherSnapshot; - final long token = Binder.clearCallingIdentity(); - try { - tetherSnapshot = getNetworkStatsTethering(STATS_PER_IFACE); - } catch (RemoteException e) { - Slog.w(TAG, "Error get TetherStats: " + e); - return 0; - } finally { - Binder.restoreCallingIdentity(token); - } - HashSet<String> limitIfaces; + private long getProviderIfaceStats(@Nullable String iface, int type) { + final NetworkStats providerSnapshot = getNetworkStatsFromProviders(STATS_PER_IFACE); + final HashSet<String> limitIfaces; if (iface == IFACE_ALL) { limitIfaces = null; } else { - limitIfaces = new HashSet<String>(); + limitIfaces = new HashSet<>(); limitIfaces.add(iface); } - NetworkStats.Entry entry = tetherSnapshot.getTotal(null, limitIfaces); - if (LOGD) Slog.d(TAG, "TetherStats: iface=" + iface + " type=" + type + - " entry=" + entry); + final NetworkStats.Entry entry = providerSnapshot.getTotal(null, limitIfaces); switch (type) { - case 0: // TYPE_RX_BYTES + case TrafficStats.TYPE_RX_BYTES: return entry.rxBytes; - case 1: // TYPE_RX_PACKETS + case TrafficStats.TYPE_RX_PACKETS: return entry.rxPackets; - case 2: // TYPE_TX_BYTES + case TrafficStats.TYPE_TX_BYTES: return entry.txBytes; - case 3: // TYPE_TX_PACKETS + case TrafficStats.TYPE_TX_PACKETS: return entry.txPackets; default: return 0; @@ -1429,14 +1416,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub { final NetworkStats devSnapshot = readNetworkStatsSummaryDev(); Trace.traceEnd(TRACE_TAG_NETWORK); - // Tethering snapshot for dev and xt stats. Counts per-interface data from tethering stats - // providers that isn't already counted by dev and XT stats. - Trace.traceBegin(TRACE_TAG_NETWORK, "snapshotTether"); - final NetworkStats tetherSnapshot = getNetworkStatsTethering(STATS_PER_IFACE); - Trace.traceEnd(TRACE_TAG_NETWORK); - xtSnapshot.combineAllValues(tetherSnapshot); - devSnapshot.combineAllValues(tetherSnapshot); - // Snapshot for dev/xt stats from all custom stats providers. Counts per-interface data // from stats providers that isn't already counted by dev and XT stats. Trace.traceBegin(TRACE_TAG_NETWORK, "snapshotStatsProvider"); @@ -1511,29 +1490,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { final boolean persistUid = (flags & FLAG_PERSIST_UID) != 0; final boolean persistForce = (flags & FLAG_PERSIST_FORCE) != 0; - // Request asynchronous stats update from all providers for next poll. And wait a bit of - // time to allow providers report-in given that normally binder call should be fast. Note - // that size of list might be changed because addition/removing at the same time. For - // addition, the stats of the missed provider can only be collected in next poll; - // for removal, wait might take up to MAX_STATS_PROVIDER_POLL_WAIT_TIME_MS - // once that happened. - // TODO: request with a valid token. - Trace.traceBegin(TRACE_TAG_NETWORK, "provider.requestStatsUpdate"); - final int registeredCallbackCount = mStatsProviderCbList.size(); - mStatsProviderSem.drainPermits(); - invokeForAllStatsProviderCallbacks( - (cb) -> cb.mProvider.onRequestStatsUpdate(0 /* unused */)); - try { - mStatsProviderSem.tryAcquire(registeredCallbackCount, - MAX_STATS_PROVIDER_POLL_WAIT_TIME_MS, TimeUnit.MILLISECONDS); - } catch (InterruptedException e) { - // Strictly speaking it's possible a provider happened to deliver between the timeout - // and the log, and that doesn't matter too much as this is just a debug log. - Log.d(TAG, "requestStatsUpdate - providers responded " - + mStatsProviderSem.availablePermits() - + "/" + registeredCallbackCount + " : " + e); - } - Trace.traceEnd(TRACE_TAG_NETWORK); + performPollFromProvidersLocked(); // TODO: consider marking "untrusted" times in historical stats final long currentTime = mClock.millis(); @@ -1578,6 +1535,33 @@ public class NetworkStatsService extends INetworkStatsService.Stub { Trace.traceEnd(TRACE_TAG_NETWORK); } + @GuardedBy("mStatsLock") + private void performPollFromProvidersLocked() { + // Request asynchronous stats update from all providers for next poll. And wait a bit of + // time to allow providers report-in given that normally binder call should be fast. Note + // that size of list might be changed because addition/removing at the same time. For + // addition, the stats of the missed provider can only be collected in next poll; + // for removal, wait might take up to MAX_STATS_PROVIDER_POLL_WAIT_TIME_MS + // once that happened. + // TODO: request with a valid token. + Trace.traceBegin(TRACE_TAG_NETWORK, "provider.requestStatsUpdate"); + final int registeredCallbackCount = mStatsProviderCbList.size(); + mStatsProviderSem.drainPermits(); + invokeForAllStatsProviderCallbacks( + (cb) -> cb.mProvider.onRequestStatsUpdate(0 /* unused */)); + try { + mStatsProviderSem.tryAcquire(registeredCallbackCount, + MAX_STATS_PROVIDER_POLL_WAIT_TIME_MS, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + // Strictly speaking it's possible a provider happened to deliver between the timeout + // and the log, and that doesn't matter too much as this is just a debug log. + Log.d(TAG, "requestStatsUpdate - providers responded " + + mStatsProviderSem.availablePermits() + + "/" + registeredCallbackCount + " : " + e); + } + Trace.traceEnd(TRACE_TAG_NETWORK); + } + /** * Sample recent statistics summary into {@link EventLog}. */ @@ -1931,9 +1915,13 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } /** - * Return snapshot of current tethering statistics. Will return empty - * {@link NetworkStats} if any problems are encountered. + * Return snapshot of current non-offloaded tethering statistics. Will return empty + * {@link NetworkStats} if any problems are encountered, or queried by {@code STATS_PER_IFACE} + * since it is already included by {@link #nativeGetIfaceStat}. + * See {@code OffloadTetheringStatsProvider} for offloaded tethering stats. */ + // TODO: Remove this by implementing {@link NetworkStatsProvider} for non-offloaded + // tethering stats. private NetworkStats getNetworkStatsTethering(int how) throws RemoteException { try { return mNetworkManager.getNetworkStatsTethering(how); @@ -2226,13 +2214,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } } - private static int TYPE_RX_BYTES; - private static int TYPE_RX_PACKETS; - private static int TYPE_TX_BYTES; - private static int TYPE_TX_PACKETS; - private static int TYPE_TCP_RX_PACKETS; - private static int TYPE_TCP_TX_PACKETS; - private static native long nativeGetTotalStat(int type, boolean useBpfStats); private static native long nativeGetIfaceStat(String iface, int type, boolean useBpfStats); private static native long nativeGetUidStat(int uid, int type, boolean useBpfStats); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 3d6360696686..7fa322559d46 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -10564,7 +10564,7 @@ public class PackageManagerService extends IPackageManager.Stub continue; } final PackageSetting staticLibPkgSetting = getPackageSetting( - toStaticSharedLibraryPackageName(sharedLibraryInfo.getPackageName(), + toStaticSharedLibraryPackageName(sharedLibraryInfo.getName(), sharedLibraryInfo.getLongVersion())); if (staticLibPkgSetting == null) { Slog.wtf(TAG, "Shared lib without setting: " + sharedLibraryInfo); diff --git a/services/core/jni/com_android_server_net_NetworkStatsService.cpp b/services/core/jni/com_android_server_net_NetworkStatsService.cpp index 0275f3ea32f7..10b248a70e7e 100644 --- a/services/core/jni/com_android_server_net_NetworkStatsService.cpp +++ b/services/core/jni/com_android_server_net_NetworkStatsService.cpp @@ -215,21 +215,6 @@ static const JNINativeMethod gMethods[] = { }; int register_android_server_net_NetworkStatsService(JNIEnv* env) { - jclass netStatsService = env->FindClass("com/android/server/net/NetworkStatsService"); - jfieldID rxBytesId = env->GetStaticFieldID(netStatsService, "TYPE_RX_BYTES", "I"); - jfieldID rxPacketsId = env->GetStaticFieldID(netStatsService, "TYPE_RX_PACKETS", "I"); - jfieldID txBytesId = env->GetStaticFieldID(netStatsService, "TYPE_TX_BYTES", "I"); - jfieldID txPacketsId = env->GetStaticFieldID(netStatsService, "TYPE_TX_PACKETS", "I"); - jfieldID tcpRxPacketsId = env->GetStaticFieldID(netStatsService, "TYPE_TCP_RX_PACKETS", "I"); - jfieldID tcpTxPacketsId = env->GetStaticFieldID(netStatsService, "TYPE_TCP_TX_PACKETS", "I"); - - env->SetStaticIntField(netStatsService, rxBytesId, RX_BYTES); - env->SetStaticIntField(netStatsService, rxPacketsId, RX_PACKETS); - env->SetStaticIntField(netStatsService, txBytesId, TX_BYTES); - env->SetStaticIntField(netStatsService, txPacketsId, TX_PACKETS); - env->SetStaticIntField(netStatsService, tcpRxPacketsId, TCP_RX_PACKETS); - env->SetStaticIntField(netStatsService, tcpTxPacketsId, TCP_TX_PACKETS); - return jniRegisterNativeMethods(env, "com/android/server/net/NetworkStatsService", gMethods, NELEM(gMethods)); } diff --git a/services/net/Android.bp b/services/net/Android.bp index 72ad36677c23..29bf37460568 100644 --- a/services/net/Android.bp +++ b/services/net/Android.bp @@ -22,13 +22,14 @@ java_library_static { // Version of services.net for usage by the wifi mainline module. // Note: This is compiled against module_current. -// TODO(b/145825329): This should be moved to networkstack-client, +// TODO(b/172457099): This should be moved to networkstack-client, // with dependencies moved to frameworks/libs/net right. java_library { name: "services.net-module-wifi", srcs: [ ":framework-services-net-module-wifi-shared-srcs", ":net-module-utils-srcs", + ":net-utils-services-common-srcs", "java/android/net/ip/IpClientCallbacks.java", "java/android/net/ip/IpClientManager.java", "java/android/net/ip/IpClientUtil.java", @@ -39,6 +40,7 @@ java_library { "java/android/net/TcpKeepalivePacketData.java", ], sdk_version: "module_current", + min_sdk_version: "30", libs: [ "unsupportedappusage", "framework-wifi-util-lib", @@ -49,7 +51,6 @@ java_library { "netd_aidl_interface-V3-java", "netlink-client", "networkstack-client", - "net-utils-services-common", ], apex_available: [ "com.android.wifi", diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java index 19449654f2ec..0d878b401bee 100644 --- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java +++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java @@ -28,7 +28,6 @@ import android.os.IBinder.DeathRecipient; import android.os.Looper; import android.os.RemoteException; import android.os.ServiceManager; -import android.os.SystemProperties; import android.os.UpdateEngine; import android.os.UpdateEngineCallback; import android.provider.DeviceConfig; @@ -227,8 +226,8 @@ public final class ProfcollectForwardingService extends SystemService { } // Sample for a fraction of app launches. - int traceFrequency = - SystemProperties.getInt("persist.profcollectd.applaunch_trace_freq", 2); + int traceFrequency = DeviceConfig.getInt(DeviceConfig.NAMESPACE_PROFCOLLECT_NATIVE_BOOT, + "applaunch_trace_freq", 2); int randomNum = ThreadLocalRandom.current().nextInt(100); if (randomNum < traceFrequency) { try { diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java index bbf34df8fe84..724a9e477b95 100644 --- a/telecomm/java/android/telecom/Connection.java +++ b/telecomm/java/android/telecom/Connection.java @@ -767,6 +767,19 @@ public abstract class Connection extends Conferenceable { "android.telecom.extra.AUDIO_CODEC"; /** + * Float connection extra key used to store the audio codec bitrate in kbps for the current + * {@link Connection}. + */ + public static final String EXTRA_AUDIO_CODEC_BITRATE_KBPS = + "android.telecom.extra.AUDIO_CODEC_BITRATE_KBPS"; + + /** + * Float connection extra key used to store the audio codec bandwidth in khz for the current + * {@link Connection}. + */ + public static final String EXTRA_AUDIO_CODEC_BANDWIDTH_KHZ = + "android.telecom.extra.AUDIO_CODEC_BANDWIDTH_KHZ"; + /** * Connection event used to inform Telecom that it should play the on hold tone. This is used * to play a tone when the peer puts the current call on hold. Sent to Telecom via * {@link #sendConnectionEvent(String, Bundle)}. diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index 724c17177f76..fbb1701dd52a 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -32,6 +32,7 @@ import android.content.Intent; import android.net.Uri; import android.os.Build; import android.os.Bundle; +import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; @@ -1589,6 +1590,30 @@ public class TelecomManager { } /** + * Returns whether the caller has {@link InCallService} access for companion apps. + * + * A companion app is an app associated with a physical wearable device via the + * {@link android.companion.CompanionDeviceManager} API. + * + * @return {@code true} if the caller has {@link InCallService} access for + * companion app; {@code false} otherwise. + */ + public boolean hasCompanionInCallServiceAccess() { + try { + if (isServiceConnected()) { + return getTelecomService().hasCompanionInCallServiceAccess( + mContext.getOpPackageName()); + } + } catch (RemoteException e) { + Log.e(TAG, "RemoteException calling hasCompanionInCallServiceAccess().", e); + if (!isSystemProcess()) { + e.rethrowAsRuntimeException(); + } + } + return false; + } + + /** * Returns whether there is an ongoing call originating from a managed * {@link ConnectionService}. An ongoing call can be in dialing, ringing, active or holding * states. @@ -2384,6 +2409,10 @@ public class TelecomManager { } } + private boolean isSystemProcess() { + return Process.myUid() == Process.SYSTEM_UID; + } + private ITelecomService getTelecomService() { if (mTelecomServiceOverride != null) { return mTelecomServiceOverride; diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl index a28a999e8d19..6dc096daf4ea 100644 --- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl +++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl @@ -179,6 +179,11 @@ interface ITelecomService { boolean isInCall(String callingPackage, String callingFeatureId); /** + * @see TelecomServiceImpl#hasCompanionInCallServiceAccess + */ + boolean hasCompanionInCallServiceAccess(String callingPackage); + + /** * @see TelecomServiceImpl#isInManagedCall */ boolean isInManagedCall(String callingPackage, String callingFeatureId); diff --git a/telephony/common/com/android/internal/telephony/CarrierAppUtils.java b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java index 7aecfdde71bc..d1412b772eef 100644 --- a/telephony/common/com/android/internal/telephony/CarrierAppUtils.java +++ b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java @@ -32,6 +32,7 @@ import android.util.ArrayMap; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.telephony.util.TelephonyUtils; import java.util.ArrayList; import java.util.List; @@ -314,7 +315,7 @@ public final class CarrierAppUtils { String[] packageNames = new String[enabledCarrierPackages.size()]; enabledCarrierPackages.toArray(packageNames); permissionManager.grantDefaultPermissionsToEnabledCarrierApps(packageNames, - UserHandle.of(userId), Runnable::run, isSuccess -> { }); + UserHandle.of(userId), TelephonyUtils.DIRECT_EXECUTOR, isSuccess -> { }); } } catch (PackageManager.NameNotFoundException e) { Log.w(TAG, "Could not reach PackageManager", e); diff --git a/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java index 7736473feafb..02d741011220 100644 --- a/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java +++ b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java @@ -34,6 +34,7 @@ import java.io.PrintWriter; import java.util.Collections; import java.util.List; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; @@ -44,6 +45,8 @@ public final class TelephonyUtils { public static boolean IS_USER = "user".equals(android.os.Build.TYPE); public static boolean IS_DEBUGGABLE = SystemProperties.getInt("ro.debuggable", 0) == 1; + public static final Executor DIRECT_EXECUTOR = Runnable::run; + /** * Verify that caller holds {@link android.Manifest.permission#DUMP}. * diff --git a/telephony/java/android/telephony/CarrierBandwidth.aidl b/telephony/java/android/telephony/CarrierBandwidth.aidl new file mode 100644 index 000000000000..d0861b88e737 --- /dev/null +++ b/telephony/java/android/telephony/CarrierBandwidth.aidl @@ -0,0 +1,17 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.telephony; +parcelable CarrierBandwidth;
\ No newline at end of file diff --git a/telephony/java/android/telephony/CarrierBandwidth.java b/telephony/java/android/telephony/CarrierBandwidth.java new file mode 100644 index 000000000000..17747a3919ee --- /dev/null +++ b/telephony/java/android/telephony/CarrierBandwidth.java @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + +/** + * Defines downlink and uplink capacity of a network in kbps + * @hide + */ +@SystemApi +public final class CarrierBandwidth implements Parcelable { + /** + * Any field that is not reported shall be set to INVALID + */ + public static final int INVALID = -1; + + /** + * Estimated downlink capacity in kbps of the primary carrier. + * This bandwidth estimate shall be the estimated maximum sustainable link bandwidth. + * This will be {@link #INVALID} if the network is not connected + */ + private int mPrimaryDownlinkCapacityKbps; + + /** + * Estimated uplink capacity in kbps of the primary carrier. + * This bandwidth estimate shall be the estimated maximum sustainable link bandwidth. + * This will be {@link #INVALID} if the network is not connected + */ + private int mPrimaryUplinkCapacityKbps; + + /** + * Estimated downlink capacity in kbps of the secondary carrier in a dual connected network. + * This bandwidth estimate shall be the estimated maximum sustainable link bandwidth. + * This will be {@link #INVALID} if the network is not connected + */ + private int mSecondaryDownlinkCapacityKbps; + + /** + * Estimated uplink capacity in kbps of the secondary carrier in a dual connected network. + * This bandwidth estimate shall be the estimated maximum sustainable link bandwidth. + * This will be {@link #INVALID} if the network is not connected + */ + private int mSecondaryUplinkCapacityKbps; + + /** @hide **/ + public CarrierBandwidth(Parcel in) { + mPrimaryDownlinkCapacityKbps = in.readInt(); + mPrimaryUplinkCapacityKbps = in.readInt(); + mSecondaryDownlinkCapacityKbps = in.readInt(); + mSecondaryUplinkCapacityKbps = in.readInt(); + } + + /** @hide **/ + public CarrierBandwidth() { + mPrimaryDownlinkCapacityKbps = INVALID; + mPrimaryUplinkCapacityKbps = INVALID; + mSecondaryDownlinkCapacityKbps = INVALID; + mSecondaryUplinkCapacityKbps = INVALID; + } + + /** + * Constructor. + * + * @param primaryDownlinkCapacityKbps Estimated downlink capacity in kbps of + * the primary carrier. + * @param primaryUplinkCapacityKbps Estimated uplink capacity in kbps of + * the primary carrier. + * @param secondaryDownlinkCapacityKbps Estimated downlink capacity in kbps of + * the secondary carrier + * @param secondaryUplinkCapacityKbps Estimated uplink capacity in kbps of + * the secondary carrier + */ + public CarrierBandwidth(int primaryDownlinkCapacityKbps, int primaryUplinkCapacityKbps, + int secondaryDownlinkCapacityKbps, int secondaryUplinkCapacityKbps) { + mPrimaryDownlinkCapacityKbps = primaryDownlinkCapacityKbps; + mPrimaryUplinkCapacityKbps = primaryUplinkCapacityKbps; + mSecondaryDownlinkCapacityKbps = secondaryDownlinkCapacityKbps; + mSecondaryUplinkCapacityKbps = secondaryUplinkCapacityKbps; + } + + /** + * Retrieves the upstream bandwidth for the primary network in Kbps. This always only refers to + * the estimated first hop transport bandwidth. + * This will be INVALID if the network is not connected + * + * @return The estimated first hop upstream (device to network) bandwidth. + */ + public int getPrimaryDownlinkCapacityKbps() { + return mPrimaryDownlinkCapacityKbps; + } + + /** + * Retrieves the downstream bandwidth for the primary network in Kbps. This always only refers + * to the estimated first hop transport bandwidth. + * This will be INVALID if the network is not connected + * + * @return The estimated first hop downstream (network to device) bandwidth. + */ + public int getPrimaryUplinkCapacityKbps() { + return mPrimaryUplinkCapacityKbps; + } + + /** + * Retrieves the upstream bandwidth for the secondary network in Kbps. This always only refers + * to the estimated first hop transport bandwidth. + * This will be INVALID if the network is not connected + * + * @return The estimated first hop upstream (device to network) bandwidth. + */ + public int getSecondaryDownlinkCapacityKbps() { + return mSecondaryDownlinkCapacityKbps; + } + + /** + * Retrieves the downstream bandwidth for the secondary network in Kbps. This always only + * refers to the estimated first hop transport bandwidth. + * This will be INVALID if the network is not connected + * + * @return The estimated first hop downstream (network to device) bandwidth. + */ + public int getSecondaryUplinkCapacityKbps() { + return mSecondaryUplinkCapacityKbps; + } + + @NonNull + @Override + public String toString() { + return "CarrierBandwidth: {primaryDownlinkCapacityKbps=" + mPrimaryDownlinkCapacityKbps + + " primaryUplinkCapacityKbps=" + mPrimaryUplinkCapacityKbps + + " secondaryDownlinkCapacityKbps=" + mSecondaryDownlinkCapacityKbps + + " secondaryUplinkCapacityKbps=" + mSecondaryUplinkCapacityKbps + + "}"; + } + + @Override + public int hashCode() { + return Objects.hash( + mPrimaryDownlinkCapacityKbps, + mPrimaryUplinkCapacityKbps, + mSecondaryDownlinkCapacityKbps, + mSecondaryUplinkCapacityKbps); + } + + @Override + public boolean equals(@Nullable Object o) { + if (o == null || !(o instanceof CallQuality) || hashCode() != o.hashCode()) { + return false; + } + if (this == o) { + return true; + } + CarrierBandwidth s = (CarrierBandwidth) o; + return (mPrimaryDownlinkCapacityKbps == s.mPrimaryDownlinkCapacityKbps + && mPrimaryUplinkCapacityKbps == s.mPrimaryUplinkCapacityKbps + && mSecondaryDownlinkCapacityKbps == s.mSecondaryDownlinkCapacityKbps + && mSecondaryDownlinkCapacityKbps == s.mSecondaryDownlinkCapacityKbps); + } + + /** + * {@link Parcelable#describeContents} + */ + public int describeContents() { + return 0; + } + + /** + * {@link Parcelable#writeToParcel} + * @hide + */ + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mPrimaryDownlinkCapacityKbps); + dest.writeInt(mPrimaryUplinkCapacityKbps); + dest.writeInt(mSecondaryDownlinkCapacityKbps); + dest.writeInt(mSecondaryUplinkCapacityKbps); + } + + public static final @android.annotation.NonNull Parcelable.Creator<CarrierBandwidth> CREATOR = + new Parcelable.Creator() { + public CarrierBandwidth createFromParcel(Parcel in) { + return new CarrierBandwidth(in); + } + + public CarrierBandwidth[] newArray(int size) { + return new CarrierBandwidth[size]; + } + }; +} diff --git a/telephony/java/android/telephony/ImsManager.java b/telephony/java/android/telephony/ImsManager.java index 28feab27a794..42d7707c97d9 100644 --- a/telephony/java/android/telephony/ImsManager.java +++ b/telephony/java/android/telephony/ImsManager.java @@ -22,7 +22,12 @@ import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.SystemService; import android.content.Context; +import android.telephony.BinderCacheManager; import android.telephony.SubscriptionManager; +import android.telephony.TelephonyFrameworkInitializer; +import android.telephony.ims.aidl.IImsRcsController; + +import com.android.internal.telephony.ITelephony; /** * Provides access to information about Telephony IMS services on the device. @@ -30,8 +35,6 @@ import android.telephony.SubscriptionManager; @SystemService(Context.TELEPHONY_IMS_SERVICE) public class ImsManager { - private Context mContext; - /** * <p>Broadcast Action: Indicates that a previously allowed IMS operation was rejected by the * network due to the network returning a "forbidden" response. This may be due to a @@ -87,6 +90,14 @@ public class ImsManager { public static final String EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE = "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_MESSAGE"; + // Cache Telephony Binder interfaces, one cache per process. + private static final BinderCacheManager<ITelephony> sTelephonyCache = + new BinderCacheManager<>(ImsManager::getITelephonyInterface); + private static final BinderCacheManager<IImsRcsController> sRcsCache = + new BinderCacheManager<>(ImsManager::getIImsRcsControllerInterface); + + private final Context mContext; + /** * Use {@link Context#getSystemService(String)} to get an instance of this class. * @hide @@ -108,7 +119,7 @@ public class ImsManager { throw new IllegalArgumentException("Invalid subscription ID: " + subscriptionId); } - return new ImsRcsManager(mContext, subscriptionId); + return new ImsRcsManager(mContext, subscriptionId, sRcsCache); } /** @@ -124,17 +135,19 @@ public class ImsManager { throw new IllegalArgumentException("Invalid subscription ID: " + subscriptionId); } - return new ImsMmTelManager(subscriptionId); + return new ImsMmTelManager(subscriptionId, sTelephonyCache); } /** - * Create an instance of SipDelegateManager for the subscription id specified. + * Create an instance of {@link SipDelegateManager} for the subscription id specified. * <p> - * Used for RCS single registration cases, where an IMS application needs to forward SIP - * traffic through the device's IMS service. - * @param subscriptionId The ID of the subscription that this SipDelegateManager will use. + * Allows an IMS application to forward SIP traffic through the device's IMS service, + * which is used for cellular carriers that require the device to share a single IMS + * registration for both MMTEL and RCS features. + * @param subscriptionId The ID of the subscription that this {@link SipDelegateManager} will + * be bound to. * @throws IllegalArgumentException if the subscription is invalid. - * @return a SipDelegateManager instance for the specified subscription ID. + * @return a {@link SipDelegateManager} instance for the specified subscription ID. * @hide */ @SystemApi @@ -144,6 +157,22 @@ public class ImsManager { throw new IllegalArgumentException("Invalid subscription ID: " + subscriptionId); } - return new SipDelegateManager(mContext, subscriptionId); + return new SipDelegateManager(mContext, subscriptionId, sRcsCache); + } + + private static IImsRcsController getIImsRcsControllerInterface() { + return IImsRcsController.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getTelephonyImsServiceRegisterer() + .get()); + } + + private static ITelephony getITelephonyInterface() { + return ITelephony.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getTelephonyServiceRegisterer() + .get()); } } diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 88aec5184548..27aadd56dac5 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -13440,6 +13440,33 @@ public class TelephonyManager { } /** + * Get carrier bandwidth. In case of Dual connected network this will report + * bandwidth per primary and secondary network. + * @return CarrierBandwidth with bandwidth of both primary and secondary carrier. + * @throws IllegalStateException if the Telephony process is not currently available. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @NonNull + public CarrierBandwidth getCarrierBandwidth() { + try { + ITelephony service = getITelephony(); + if (service != null) { + return service.getCarrierBandwidth(getSubId()); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + Log.e(TAG, "getCarrierBandwidth RemoteException", ex); + ex.rethrowFromSystemServer(); + } + + //Should not reach. Adding return statement to make compiler happy + return null; + } + + /** * Called when userActivity is signalled in the power manager. * This should only be called from system Uid. * @hide diff --git a/telephony/java/android/telephony/ims/DelegateMessageCallback.java b/telephony/java/android/telephony/ims/DelegateMessageCallback.java new file mode 100644 index 000000000000..beec4a680d78 --- /dev/null +++ b/telephony/java/android/telephony/ims/DelegateMessageCallback.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims; + +import android.annotation.NonNull; +import android.telephony.ims.stub.SipDelegate; + +/** + * Callback interface provided to the SipTransport implementation to notify a remote application of + * the following: + * <ul> + * <li>A new incoming SIP message associated with the feature tags the SipDelegate registered + * with has been received or an in-dialog request to this SipDelegate has been received.</li> + * <li>Acknowledge that an outgoing SIP message from the RCS application has been sent + * successfully or notify the application of the reason why it was not sent</li> + * </ul> + * @hide + */ +public interface DelegateMessageCallback { + + /** + * Send a new incoming SIP message to the remote application for processing. + */ + void onMessageReceived(@NonNull SipMessage message); + + /** + * Notify the remote application that a previous request to send a SIP message using + * {@link SipDelegate#sendMessage} has succeeded. + * + * @param viaTransactionId The transaction ID found in the via header field of the + * previously sent {@link SipMessage}. + */ + void onMessageSent(@NonNull String viaTransactionId); + + /** + * Notify the remote application that a previous request to send a SIP message using + * {@link SipDelegate#sendMessage} has failed. + * + * @param viaTransactionId The Transaction ID found in the via header field of the previously + * sent {@link SipMessage}. + * @param reason The reason for the failure. + */ + void onMessageSendFailure(@NonNull String viaTransactionId, + @SipDelegateManager.MessageFailureReason int reason); +} diff --git a/telephony/java/android/telephony/ims/DelegateRegistrationState.aidl b/telephony/java/android/telephony/ims/DelegateRegistrationState.aidl new file mode 100644 index 000000000000..756ea920d06a --- /dev/null +++ b/telephony/java/android/telephony/ims/DelegateRegistrationState.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims; + +parcelable DelegateRegistrationState; diff --git a/telephony/java/android/telephony/ims/DelegateRegistrationState.java b/telephony/java/android/telephony/ims/DelegateRegistrationState.java new file mode 100644 index 000000000000..4facfa77de21 --- /dev/null +++ b/telephony/java/android/telephony/ims/DelegateRegistrationState.java @@ -0,0 +1,327 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.ArraySet; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +/** + * Contains the full state of the IMS feature tags associated with a SipDelegate and managed by the + * ImsService. + * @hide + */ +public final class DelegateRegistrationState implements Parcelable { + + /** + * This feature tag has been deregistered for an unknown reason. Outgoing out-of-dialog SIP + * messages associated with feature tags that are not registered will fail. + */ + public static final int DEREGISTERED_REASON_UNKNOWN = 0; + + /** + * This feature tag has been deregistered because it is not provisioned to be used on this radio + * access technology or PDN. Outgoing out-of-dialog SIP messages associated with feature tags + * that are not registered will fail. + * <p> + * There may be new incoming SIP dialog requests on a feature that that is not provisioned. It + * is still expected that the SipDelegateConnection responds to the request. + */ + public static final int DEREGISTERED_REASON_NOT_PROVISIONED = 1; + + /** + * This feature tag has been deregistered because IMS has been deregistered. All outgoing SIP + * messages will fail until IMS registration occurs. + */ + public static final int DEREGISTERED_REASON_NOT_REGISTERED = 2; + + /** + * This feature tag is being deregistered because the PDN that the IMS registration is on is + *changing. + * All open SIP dialogs need to be closed before the PDN change can proceed. + */ + public static final int DEREGISTERING_REASON_PDN_CHANGE = 3; + + /** + * This feature tag is being deregistered due to a provisioning change. This can be triggered by + * many things, such as a provisioning change triggered by the carrier network, a radio access + * technology change by the modem causing a different set of feature tags to be provisioned, or + * a user triggered hange, such as data being enabled/disabled. + * <p> + * All open SIP dialogs associated with the new deprovisioned feature tag need to be closed + * before the IMS registration modification can proceed. + */ + public static final int DEREGISTERING_REASON_PROVISIONING_CHANGE = 4; + + /** + * This feature tag is deregistering because the SipDelegate associated with this feature tag + * needs to change its supported feature set. + * <p> + * All open SIP Dialogs associated with this feature tag must be closed before this operation + * can proceed. + */ + public static final int DEREGISTERING_REASON_FEATURE_TAGS_CHANGING = 5; + + /** + * This feature tag is deregistering because the SipDelegate is in the process of being + * destroyed. + * <p> + * All open SIP Dialogs associated with this feature tag must be closed before this operation + * can proceed. + */ + public static final int DEREGISTERING_REASON_DESTROY_PENDING = 6; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "DEREGISTERED_REASON_", value = { + DEREGISTERED_REASON_UNKNOWN, + DEREGISTERED_REASON_NOT_PROVISIONED, + DEREGISTERED_REASON_NOT_REGISTERED + }) + public @interface DeregisteredReason {} + + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "DEREGISTERING_REASON_", value = { + DEREGISTERING_REASON_PDN_CHANGE, + DEREGISTERING_REASON_PROVISIONING_CHANGE, + DEREGISTERING_REASON_FEATURE_TAGS_CHANGING, + DEREGISTERING_REASON_DESTROY_PENDING + }) + public @interface DeregisteringReason {} + + private final ArrayList<String> mRegisteredTags = new ArrayList<>(); + private final ArrayList<FeatureTagState> mDeregisteringTags = new ArrayList<>(); + private final ArrayList<FeatureTagState> mDeregisteredTags = new ArrayList<>(); + + /** + * Builder used to create new instances of {@link DelegateRegistrationState}. + */ + public static class Builder { + + private final DelegateRegistrationState mState; + + /* Create a new instance of {@link Builder} */ + public Builder() { + mState = new DelegateRegistrationState(); + } + + /** + * Add a feature tag that is currently included in the current network IMS Registration. + * @param featureTag The IMS media feature tag included in the current IMS registration. + * @return The in-progress Builder instance for RegistrationState. + */ + public Builder addRegisteredFeatureTag(@NonNull String featureTag) { + if (!mState.mRegisteredTags.contains(featureTag)) { + mState.mRegisteredTags.add(featureTag); + } + return this; + } + + /** + * Add a list of feature tags that are currently included in the current network IMS + * Registration. + * @param featureTags The IMS media feature tags included in the current IMS registration. + * @return The in-progress Builder instance for RegistrationState. + */ + public Builder addRegisteredFeatureTags(@NonNull Set<String> featureTags) { + mState.mRegisteredTags.addAll(featureTags); + return this; + } + + /** + * Add a feature tag that is in the current network IMS Registration, but is in the progress + * of being deregistered and requires action from the RCS application before the IMS + * registration can be modified. + * + * See {@link DeregisteringReason} for more information regarding what is required by the + * RCS application to proceed. + * + * @param featureTag The media feature tag that has limited or no availability due to its + * current deregistering state. + * @param reason The reason why the media feature tag has moved to the deregistering state. + * The availability of the feature tag depends on the {@link DeregisteringReason}. + * @return The in-progress Builder instance for RegistrationState. + */ + public Builder addDeregisteringFeatureTag(@NonNull String featureTag, + @DeregisteringReason int reason) { + boolean ftExists = mState.mDeregisteringTags.stream().anyMatch( + f -> f.getFeatureTag().equals(featureTag)); + if (!ftExists) { + mState.mDeregisteringTags.add(new FeatureTagState(featureTag, reason)); + } + return this; + } + + /** + * Add a feature tag that is currently not included in the network RCS registration. See + * {@link DeregisteredReason} for more information regarding the reason for why the feature + * tag is not registered. + * @param featureTag The media feature tag that is not registered. + * @param reason The reason why the media feature tag has been deregistered. + * @return The in-progress Builder instance for RegistrationState. + */ + public Builder addDeregisteredFeatureTag(@NonNull String featureTag, + @DeregisteredReason int reason) { + boolean ftExists = mState.mDeregisteredTags.stream().anyMatch( + f -> f.getFeatureTag().equals(featureTag)); + if (!ftExists) { + mState.mDeregisteredTags.add(new FeatureTagState(featureTag, reason)); + } + return this; + } + + /** + * @return the finalized instance. + */ + public DelegateRegistrationState build() { + return mState; + } + } + + /** + * The builder should be used to construct a new instance of this class. + */ + private DelegateRegistrationState() {} + + /** + * Used for unparcelling only. + */ + private DelegateRegistrationState(Parcel source) { + source.readList(mRegisteredTags, null /*classloader*/); + readStateFromParcel(source, mDeregisteringTags); + readStateFromParcel(source, mDeregisteredTags); + } + + /** + * Get the feature tags that this SipDelegate is associated with that are currently part of the + * network IMS registration. SIP Messages both in and out of a SIP Dialog may be sent and + * received using these feature tags. + * @return A Set of feature tags that the SipDelegate has associated with that are included in + * the network IMS registration. + */ + public @NonNull Set<String> getRegisteredFeatureTags() { + return new ArraySet<>(mRegisteredTags); + } + + /** + * Get the feature tags that this SipDelegate is associated with that are currently part of the + * network IMS registration but are in the process of being deregistered. + * <p> + * Any incoming SIP messages associated with a feature tag included in this list will still be + * delivered. Outgoing SIP messages that are still in-dialog will be delivered to the + * SipDelegate, but outgoing out-of-dialog SIP messages with a feature tag that is included in + * this list will fail. + * <p> + * The SipDelegate will stay in this state for a limited period of time while it waits for the + * RCS application to perform a specific action. More details on the actions that can cause this + * state as well as the expected response are included in the reason codes and can be found in + * {@link DeregisteringReason}. + * @return A Set of feature tags that the SipDelegate has associated with that are included in + * the network IMS registration but are in the process of deregistering. + */ + public @NonNull Set<FeatureTagState> getDeregisteringFeatureTags() { + return new ArraySet<>(mDeregisteringTags); + } + + /** + * Get the list of feature tags that are associated with this SipDelegate but are not currently + * included in the network IMS registration. + * <p> + * See {@link DeregisteredReason} codes for more information related to the reasons why this may + * occur. + * <p> + * Due to network race conditions, there may still be onditions where an incoming out-of-dialog + * SIP message is delivered for a feature tag that is considered deregistered. Due to this + * condition, in-dialog outgoing SIP messages for deregistered feature tags will still be + * allowed as long as they are in response to a dialog started by a remote party. Any outgoing + * out-of-dialog SIP messages associated with feature tags included in this list will fail to be + * sent. + * @return A list of feature tags that the SipDelegate has associated with that not included in + * the network IMS registration. + */ + public @NonNull Set<FeatureTagState> getDeregisteredFeatureTags() { + return new ArraySet<>(mDeregisteredTags); + } + + public static final Creator<DelegateRegistrationState> CREATOR = + new Creator<DelegateRegistrationState>() { + @Override + public DelegateRegistrationState createFromParcel(Parcel source) { + return new DelegateRegistrationState(source); + } + + @Override + public DelegateRegistrationState[] newArray(int size) { + return new DelegateRegistrationState[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeList(mRegisteredTags); + writeStateToParcel(dest, mDeregisteringTags); + writeStateToParcel(dest, mDeregisteredTags); + } + + private void writeStateToParcel(Parcel dest, List<FeatureTagState> state) { + dest.writeInt(state.size()); + for (FeatureTagState s : state) { + dest.writeString(s.getFeatureTag()); + dest.writeInt(s.getState()); + } + } + + private void readStateFromParcel(Parcel source, List<FeatureTagState> emptyState) { + int len = source.readInt(); + for (int i = 0; i < len; i++) { + String ft = source.readString(); + int reason = source.readInt(); + emptyState.add(new FeatureTagState(ft, reason)); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + DelegateRegistrationState that = (DelegateRegistrationState) o; + return mRegisteredTags.equals(that.mRegisteredTags) + && mDeregisteringTags.equals(that.mDeregisteringTags) + && mDeregisteredTags.equals(that.mDeregisteredTags); + } + + @Override + public int hashCode() { + return Objects.hash(mRegisteredTags, mDeregisteringTags, mDeregisteredTags); + } +} diff --git a/telephony/java/android/telephony/ims/DelegateRequest.aidl b/telephony/java/android/telephony/ims/DelegateRequest.aidl new file mode 100644 index 000000000000..60c990f8258f --- /dev/null +++ b/telephony/java/android/telephony/ims/DelegateRequest.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims; + +parcelable DelegateRequest; diff --git a/telephony/java/android/telephony/ims/DelegateRequest.java b/telephony/java/android/telephony/ims/DelegateRequest.java new file mode 100644 index 000000000000..f384901d58bd --- /dev/null +++ b/telephony/java/android/telephony/ims/DelegateRequest.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; +import android.telephony.ims.stub.SipDelegate; +import android.util.ArraySet; + +import java.util.ArrayList; +import java.util.Objects; +import java.util.Set; + +/** + * Contains information required for the creation of a {@link SipDelegate} and the associated + * SipDelegateConnection given back to the requesting application. + * @hide + */ +public final class DelegateRequest implements Parcelable { + + private final ArrayList<String> mFeatureTags; + + /** + * Create a new DelegateRequest, which will be used to create a SipDelegate by the ImsService. + * @param featureTags The list of IMS feature tags that will be associated with the SipDelegate + * created using this DelegateRequest. All feature tags are expected to be in + * the format defined in RCC.07 section 2.6.1.3. + */ + public DelegateRequest(@NonNull Set<String> featureTags) { + if (featureTags == null) { + throw new IllegalStateException("Invalid arguments, featureTags List can not be null"); + } + mFeatureTags = new ArrayList<>(featureTags); + } + + /** + * @return the list of IMS feature tag associated with this DelegateRequest in the format + * defined in RCC.07 section 2.6.1.3. + */ + public Set<String> getFeatureTags() { + return new ArraySet<>(mFeatureTags); + } + + /** + * Internal constructor used only for unparcelling. + */ + private DelegateRequest(Parcel in) { + mFeatureTags = new ArrayList<>(); + in.readList(mFeatureTags, null /*classLoader*/); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeList(mFeatureTags); + } + + public static final @NonNull Creator<DelegateRequest> CREATOR = new Creator<DelegateRequest>() { + @Override + public DelegateRequest createFromParcel(Parcel source) { + return new DelegateRequest(source); + } + + @Override + public DelegateRequest[] newArray(int size) { + return new DelegateRequest[size]; + } + }; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + DelegateRequest that = (DelegateRequest) o; + return mFeatureTags.equals(that.mFeatureTags); + } + + @Override + public int hashCode() { + return Objects.hash(mFeatureTags); + } +} diff --git a/telephony/java/android/telephony/ims/DelegateStateCallback.java b/telephony/java/android/telephony/ims/DelegateStateCallback.java new file mode 100644 index 000000000000..0f1afc42249e --- /dev/null +++ b/telephony/java/android/telephony/ims/DelegateStateCallback.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.telephony.ims.stub.SipDelegate; +import android.telephony.ims.stub.SipTransportImplBase; + +import java.util.List; + +/** + * Callback interface to notify a remote application of the following: + * <ul> + * <li>the {@link SipDelegate} associated with this callback has been created or destroyed in + * response to a creation or destruction request from the framework</li> + * <li>the SIP IMS configuration associated with this {@link SipDelegate} has changed</li> + * <li>the IMS registration of the feature tags associated with this {@link SipDelegate} have + * changed.</li> + * </ul> + * @hide + */ +public interface DelegateStateCallback { + + /** + * This must be called by the ImsService after {@link SipTransportImplBase#createSipDelegate} is + * called by the framework to notify the framework and remote application that the + * {@link SipDelegate} has been successfully created. + * + * @param delegate The SipDelegate created to service the DelegateRequest. + * @param deniedTags A List of {@link FeatureTagState}, which contains the feature tags + * associated with this {@link SipDelegate} that have no access to send/receive SIP messages + * as well as a reason for why the feature tag is denied. For more information on the reason + * why the feature tag was denied access, see the + * {@link SipDelegateManager.DeniedReason} reasons. This is considered a permanent denial due + * to this {@link SipDelegate} not supporting a feature or this ImsService already + * implementing this feature elsewhere. If all features of this {@link SipDelegate} are + * denied, {@link #onCreated(SipDelegate, List)} should still be called as the framework will + * later call {@link SipTransportImplBase#destroySipDelegate(SipDelegate, int)} to clean the + * delegate up. + */ + void onCreated(@NonNull SipDelegate delegate, @Nullable List<FeatureTagState> deniedTags); + + /** + * This must be called by the ImsService after the framework calls + * {@link SipTransportImplBase#destroySipDelegate} to notify the framework and remote + * application that the procedure to destroy the {@link SipDelegate} has been completed. + * @param reasonCode The reason for closing this delegate. + */ + void onDestroyed(@SipDelegateManager.SipDelegateDestroyReason int reasonCode); + + /** + * Call to notify the remote application of a configuration change associated with this + * {@link SipDelegate}. + * <p> + * The remote application will not be able to proceed sending SIP messages until after this + * configuration is sent the first time, so this configuration should be sent as soon as the + * {@link SipDelegate} has access to these configuration parameters. + * <p> + * Incoming SIP messages should not be routed to the remote application until AFTER this + * configuration change is sent to ensure that the remote application can respond correctly. + * Similarly, if there is an event that triggers the IMS configuration to change, incoming SIP + * messages routing should be delayed until the {@link SipDelegate} sends the IMS configuration + * change event to reduce conditions where the remote application is using a stale IMS + * configuration. + */ + void onImsConfigurationChanged(@NonNull SipDelegateImsConfiguration config); + + /** + * Call to notify the remote application that the {@link SipDelegate} has modified the IMS + * registration state of the RCS feature tags that were requested as part of the initial + * {@link DelegateRequest}. + * <p> + * See {@link DelegateRegistrationState} for more information about how IMS Registration state + * should be communicated the associated SipDelegateConnection in cases such as + * IMS deregistration, handover, PDN change, provisioning changes, etc… + * <p> + * Note: Even after the status of the feature tags are updated here to deregistered, the + * SipDelegate must still be able to handle these messages and call + * {@link DelegateMessageCallback#onMessageSendFailure} to notify the RCS application that the + * message was not sent. + * + * @param registrationState The current network IMS registration state for all feature tags + * associated with this SipDelegate. + */ + void onFeatureTagRegistrationChanged(@NonNull DelegateRegistrationState registrationState); +} diff --git a/telephony/java/android/telephony/ims/FeatureTagState.aidl b/telephony/java/android/telephony/ims/FeatureTagState.aidl new file mode 100644 index 000000000000..bce55742461c --- /dev/null +++ b/telephony/java/android/telephony/ims/FeatureTagState.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims; + +parcelable FeatureTagState; diff --git a/telephony/java/android/telephony/ims/FeatureTagState.java b/telephony/java/android/telephony/ims/FeatureTagState.java new file mode 100644 index 000000000000..060be6f2510d --- /dev/null +++ b/telephony/java/android/telephony/ims/FeatureTagState.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; +import android.telephony.ims.stub.DelegateConnectionStateCallback; +import android.telephony.ims.stub.SipDelegate; + +import java.util.List; +import java.util.Objects; + +/** + * Maps an IMS media feature tag 3gpp universal resource name (URN) previously mapped to a + * {@link SipDelegate} in the associated {@link DelegateRequest} to its current availability + * state as set by the ImsService managing the related IMS registration. + * + * This class is only used to report more information about a IMS feature tag that is not fully + * available at this time. + * <p> + * Please see {@link DelegateRegistrationState}, {@link DelegateStateCallback}, and + * {@link DelegateConnectionStateCallback} for more information about how this class is used to + * convey the state of IMS feature tags that were requested by {@link DelegateRequest} but are not + * currently available. + * @hide + */ +public final class FeatureTagState implements Parcelable { + + private final String mFeatureTag; + private final int mState; + + /** + * Associate an IMS feature tag with its current state. See {@link DelegateRegistrationState} + * and {@link DelegateConnectionStateCallback#onFeatureTagStatusChanged( + * DelegateRegistrationState, List)} and + * {@link DelegateStateCallback#onCreated(SipDelegate, List)} for examples on how and when this + * is used. + * + * @param featureTag The IMS feature tag that is deregistered, in the process of + * deregistering, or denied. + * @param state The {@link DelegateRegistrationState.DeregisteredReason}, + * {@link DelegateRegistrationState.DeregisteringReason}, or + * {@link SipDelegateManager.DeniedReason} associated with this feature tag. + */ + public FeatureTagState(@NonNull String featureTag, int state) { + mFeatureTag = featureTag; + mState = state; + } + + /** + * Used for constructing instances during un-parcelling. + */ + private FeatureTagState(Parcel source) { + mFeatureTag = source.readString(); + mState = source.readInt(); + } + + /** + * @return The IMS feature tag string that is in the process of deregistering, + * deregistered, or denied. + */ + public @NonNull String getFeatureTag() { + return mFeatureTag; + } + + /** + * @return The reason for why the feature tag is currently in the process of deregistering, + * has been deregistered, or has been denied. See {@link DelegateRegistrationState} and + * {@link DelegateConnectionStateCallback} for more information. + */ + public int getState() { + return mState; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mFeatureTag); + dest.writeInt(mState); + } + + public static final Creator<FeatureTagState> CREATOR = new Creator<FeatureTagState>() { + @Override + public FeatureTagState createFromParcel(Parcel source) { + return new FeatureTagState(source); + } + + @Override + public FeatureTagState[] newArray(int size) { + return new FeatureTagState[size]; + } + }; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + FeatureTagState that = (FeatureTagState) o; + return mState == that.mState + && mFeatureTag.equals(that.mFeatureTag); + } + + @Override + public int hashCode() { + return Objects.hash(mFeatureTag, mState); + } + + @Override + public String toString() { + return "FeatureTagState{" + "mFeatureTag='" + mFeatureTag + ", mState=" + mState + '}'; + } +} diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java index 9a55cec80816..1b51936e873b 100644 --- a/telephony/java/android/telephony/ims/ImsCallProfile.java +++ b/telephony/java/android/telephony/ims/ImsCallProfile.java @@ -450,8 +450,6 @@ public final class ImsCallProfile implements Parcelable { /** Indicates if we have known the intent of the user for the call is emergency */ private boolean mHasKnownUserIntentEmergency = false; - private Set<RtpHeaderExtensionType> mOfferedRtpHeaderExtensionTypes = new ArraySet<>(); - private Set<RtpHeaderExtensionType> mAcceptedRtpHeaderExtensionTypes = new ArraySet<>(); /** @@ -692,7 +690,6 @@ public final class ImsCallProfile implements Parcelable { out.writeBoolean(mHasKnownUserIntentEmergency); out.writeInt(mRestrictCause); out.writeInt(mCallerNumberVerificationStatus); - out.writeArray(mOfferedRtpHeaderExtensionTypes.toArray()); out.writeArray(mAcceptedRtpHeaderExtensionTypes.toArray()); } @@ -708,9 +705,6 @@ public final class ImsCallProfile implements Parcelable { mHasKnownUserIntentEmergency = in.readBoolean(); mRestrictCause = in.readInt(); mCallerNumberVerificationStatus = in.readInt(); - Object[] offered = in.readArray(RtpHeaderExtensionType.class.getClassLoader()); - mOfferedRtpHeaderExtensionTypes = Arrays.stream(offered) - .map(o -> (RtpHeaderExtensionType) o).collect(Collectors.toSet()); Object[] accepted = in.readArray(RtpHeaderExtensionType.class.getClassLoader()); mAcceptedRtpHeaderExtensionTypes = Arrays.stream(accepted) .map(o -> (RtpHeaderExtensionType) o).collect(Collectors.toSet()); @@ -1106,46 +1100,13 @@ public final class ImsCallProfile implements Parcelable { } /** - * For an incoming or outgoing call, indicates the {@link RtpHeaderExtensionType}s which the - * caller is offering to make available. - * <p> - * For outgoing calls, an {@link ImsService} will reserve - * {@link RtpHeaderExtensionType#getLocalIdentifier()} identifiers the telephony stack has - * proposed to use and not use these same local identifiers. The offered header extension - * types for an outgoing call can be found in the - * {@link ImsCallProfile#getOfferedRtpHeaderExtensionTypes()} and will be available to the - * {@link ImsService} in {@link MmTelFeature#createCallSession(ImsCallProfile)}. - * The {@link ImsService} sets the accepted {@link #setAcceptedRtpHeaderExtensionTypes(Set)} - * when the SDP offer/accept process has completed. - * <p> - * According to RFC8285, RTP header extensions available to a call are determined using the - * offer/accept phase of the SDP protocol (see RFC4566). - * - * @return the {@link RtpHeaderExtensionType}s which were offered by other end of the call. - */ - public @NonNull Set<RtpHeaderExtensionType> getOfferedRtpHeaderExtensionTypes() { - return mOfferedRtpHeaderExtensionTypes; - } - - /** - * Sets the offered {@link RtpHeaderExtensionType}s for this call. - * <p> - * According to RFC8285, RTP header extensions available to a call are determined using the - * offer/accept phase of the SDP protocol (see RFC4566). - * - * @param rtpHeaderExtensions the {@link RtpHeaderExtensionType}s which are offered. - */ - public void setOfferedRtpHeaderExtensionTypes(@NonNull Set<RtpHeaderExtensionType> - rtpHeaderExtensions) { - mOfferedRtpHeaderExtensionTypes.clear(); - mOfferedRtpHeaderExtensionTypes.addAll(rtpHeaderExtensions); - } - - /** * Gets the {@link RtpHeaderExtensionType}s which have been accepted by both ends of the call. * <p> * According to RFC8285, RTP header extensions available to a call are determined using the * offer/accept phase of the SDP protocol (see RFC4566). + * <p> + * The offered header extension types supported by the framework and exposed to the + * {@link ImsService} via {@link MmTelFeature#changeOfferedRtpHeaderExtensionTypes(Set)}. * * @return the {@link RtpHeaderExtensionType}s which were accepted by the other end of the call. */ diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java index a4f2a316c99d..d1a893f61e00 100644 --- a/telephony/java/android/telephony/ims/ImsMmTelManager.java +++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java @@ -29,6 +29,7 @@ import android.os.Binder; import android.os.RemoteException; import android.os.ServiceSpecificException; import android.telephony.AccessNetworkConstants; +import android.telephony.BinderCacheManager; import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionManager; import android.telephony.TelephonyFrameworkInitializer; @@ -213,6 +214,7 @@ public class ImsMmTelManager implements RegistrationManager { } private final int mSubId; + private final BinderCacheManager<ITelephony> mBinderCache; /** * Create an instance of {@link ImsMmTelManager} for the subscription id specified. @@ -242,7 +244,8 @@ public class ImsMmTelManager implements RegistrationManager { throw new IllegalArgumentException("Invalid subscription ID"); } - return new ImsMmTelManager(subId); + return new ImsMmTelManager(subId, new BinderCacheManager<>( + ImsMmTelManager::getITelephonyInterface)); } /** @@ -250,8 +253,9 @@ public class ImsMmTelManager implements RegistrationManager { * @hide */ @VisibleForTesting - public ImsMmTelManager(int subId) { + public ImsMmTelManager(int subId, BinderCacheManager<ITelephony> binderCache) { mSubId = subId; + mBinderCache = binderCache; } /** @@ -1367,7 +1371,11 @@ public class ImsMmTelManager implements RegistrationManager { } } - private static ITelephony getITelephony() { + private ITelephony getITelephony() { + return mBinderCache.getBinder(); + } + + private static ITelephony getITelephonyInterface() { ITelephony binder = ITelephony.Stub.asInterface( TelephonyFrameworkInitializer .getTelephonyServiceManager() diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java index 8b6dac82b0eb..4292aae59515 100644 --- a/telephony/java/android/telephony/ims/ImsRcsManager.java +++ b/telephony/java/android/telephony/ims/ImsRcsManager.java @@ -28,6 +28,7 @@ import android.os.IBinder; import android.os.RemoteException; import android.provider.Settings; import android.telephony.AccessNetworkConstants; +import android.telephony.BinderCacheManager; import android.telephony.CarrierConfigManager; import android.telephony.TelephonyFrameworkInitializer; import android.telephony.ims.aidl.IImsCapabilityCallback; @@ -149,14 +150,17 @@ public class ImsRcsManager { private final int mSubId; private final Context mContext; + private final BinderCacheManager<IImsRcsController> mBinderCache; /** * Use {@link ImsManager#getImsRcsManager(int)} to create an instance of this class. * @hide */ - public ImsRcsManager(Context context, int subId) { + public ImsRcsManager(Context context, int subId, + BinderCacheManager<IImsRcsController> binderCache) { mSubId = subId; mContext = context; + mBinderCache = binderCache; } /** diff --git a/telephony/java/android/telephony/ims/SipDelegateConnection.java b/telephony/java/android/telephony/ims/SipDelegateConnection.java new file mode 100644 index 000000000000..6bfdc2c6d48a --- /dev/null +++ b/telephony/java/android/telephony/ims/SipDelegateConnection.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims; + +import android.annotation.NonNull; +import android.telephony.ims.stub.SipDelegate; + +/** + * Represents a connection to the remote {@link SipDelegate} that is managed by the + * {@link ImsService} implementing IMS for the subscription that is associated with it. + * <p> + * The remote delegate will handle messages sent by this {@link SipDelegateConnection}, notifying + * the associated {@link DelegateMessageCallback} when the message was either sent successfully or + * failed to be sent. + * <p> + * It is also the responsibility of this {@link SipDelegateConnection} to acknowledge when incoming + * SIP messages have been received successfully via + * {@link DelegateMessageCallback#onMessageReceived(SipMessage)} or when there was an error + * receiving the message using {@link #notifyMessageReceived(String)} and + * {@link #notifyMessageReceiveError(String, int)}. + * + * @see SipDelegateManager#createSipDelegate + * @hide + */ +public interface SipDelegateConnection { + + /** + * Send a SIP message to the SIP delegate to be sent over the carrier’s network. The + * {@link SipMessage} will either be acknowledged with + * {@link DelegateMessageCallback#onMessageSent(String)} upon successful sending of this message + * or {@link DelegateMessageCallback#onMessageSendFailure(String, int)} if there was an error + * sending the message. + * @param sipMessage The SipMessage to be sent. + * @param configVersion The SipDelegateImsConfiguration version used to construct the + * SipMessage. See {@link SipDelegateImsConfiguration#getVersion} for more + * information on this parameter and why it is used. + */ + void sendMessage(@NonNull SipMessage sipMessage, int configVersion); + + /** + * Notify the {@link SipDelegate} that a SIP message received from + * {@link DelegateMessageCallback#onMessageReceived(SipMessage)} has been received successfully + * and is being processed. + * @param viaTransactionId Per RFC3261 Sec 8.1.1.7 the transaction ID associated with the Via + * branch parameter. + */ + void notifyMessageReceived(@NonNull String viaTransactionId); + + /** + * Notify the SIP delegate that the SIP message has been received from + * {@link DelegateMessageCallback#onMessageReceived(SipMessage)}, however there was an error + * processing it. + * @param viaTransactionId Per RFC3261 Sec 8.1.1.7 the transaction ID associated with the Via + * branch parameter. + * @param reason The reason why the error occurred. + */ + void notifyMessageReceiveError(@NonNull String viaTransactionId, + @SipDelegateManager.MessageFailureReason int reason); +} diff --git a/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.aidl b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.aidl new file mode 100644 index 000000000000..44ae1b130046 --- /dev/null +++ b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims; + +parcelable SipDelegateImsConfiguration; diff --git a/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java new file mode 100644 index 000000000000..8abd0ee94865 --- /dev/null +++ b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java @@ -0,0 +1,499 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims; + +import android.annotation.NonNull; +import android.annotation.StringDef; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.PersistableBundle; +import android.telephony.ims.stub.SipDelegate; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * The IMS registration and other attributes that the {@link SipDelegateConnection} used by the + * IMS application will need to be aware of to correctly generate outgoing {@link SipMessage}s. + * <p> + * The IMS service must generate new instances of this configuration as the IMS configuration + * managed by the IMS service changes. Along with each {@link SipDelegateImsConfiguration} instance + * containing the configuration is the "version", which should be incremented every time a new + * {@link SipDelegateImsConfiguration} instance is created. The {@link SipDelegateConnection} will + * include the version of the {@link SipDelegateImsConfiguration} instance that it used in order for + * the {@link SipDelegate} to easily identify if the IMS application used a now stale configuration + * to generate the {@link SipMessage} and return + * {@link SipDelegateManager#MESSAGE_FAILURE_REASON_STALE_IMS_CONFIGURATION} in + * {@link DelegateMessageCallback#onMessageSendFailure(String, int)} so that the IMS application can + * regenerate that {@link SipMessage} using the correct {@link SipDelegateImsConfiguration} + * instance. + * <p> + * Every time the IMS configuration state changes in the IMS service, a full configuration should + * be generated. The new {@link SipDelegateImsConfiguration} instance should not be an incremental + * update. + * @hide + */ +public class SipDelegateImsConfiguration implements Parcelable { + + /** + * IPV4 Address type. + * <p> + * Used as a potential value for {@link #KEY_SIP_CONFIG_IPTYPE_STRING}. + */ + public static final String IPTYPE_IPV4 = "IPV4"; + + /** + * IPV6 Address type. + * <p> + * Used as a potential value for {@link #KEY_SIP_CONFIG_IPTYPE_STRING}. + */ + public static final String IPTYPE_IPV6 = "IPV6"; + + /** + * The SIP transport uses UDP. + * <p> + * Used as a potential value for {@link #KEY_SIP_CONFIG_TRANSPORT_TYPE_STRING}. + */ + public static final String SIP_TRANSPORT_UDP = "UDP"; + + /** + * The SIP transport uses TCP. + * <p> + * Used as a potential value for {@link #KEY_SIP_CONFIG_TRANSPORT_TYPE_STRING}. + */ + public static final String SIP_TRANSPORT_TCP = "TCP"; + + /** + * Flag specifying if SIP compact form is enabled + */ + public static final String KEY_SIP_CONFIG_IS_COMPACT_FORM_ENABLED_BOOL = + "sip_config_is_compact_form_enabled_bool"; + + /** + * Flag specifying if SIP keepalives are enabled + */ + public static final String KEY_SIP_CONFIG_IS_KEEPALIVE_ENABLED_BOOL = + "sip_config_is_keepalive_enabled_bool"; + + /** + * Maximum SIP payload to be sent on UDP. If the SIP message payload is greater than max udp + * payload size, then TCP must be used + */ + public static final String KEY_SIP_CONFIG_MAX_PAYLOAD_SIZE_ON_UDP_INT = + "sip_config_udp_max_payload_size_int"; + + /** + * Transport protocol used for SIP signaling. + * Available options are: {@link #SIP_TRANSPORT_UDP }, {@link #SIP_TRANSPORT_TCP } + */ + public static final String KEY_SIP_CONFIG_TRANSPORT_TYPE_STRING = + "sip_config_protocol_type_string"; + + /** + * IMS public user identifier string + */ + public static final String KEY_SIP_CONFIG_UE_PUBLIC_USER_ID_STRING = + "sip_config_ue_public_user_id_string"; + + /** + * IMS private user identifier string + */ + public static final String KEY_SIP_CONFIG_UE_PRIVATE_USER_ID_STRING = + "sip_config_ue_private_user_id_string"; + + /** + * IMS home domain string + */ + public static final String KEY_SIP_CONFIG_HOME_DOMAIN_STRING = "sip_config_home_domain_string"; + + /** + * IMEI string. Application can include the Instance-ID feature tag " +sip.instance" in the + * Contact header with a value of the device IMEI in the form "urn:gsma:imei:<device IMEI>". + */ + public static final String KEY_SIP_CONFIG_IMEI_STRING = "sip_config_imei_string"; + + /** + * IP address type for SIP signaling. + * Available options are: {@link #IPTYPE_IPV6}, {@link #IPTYPE_IPV4} + */ + public static final String KEY_SIP_CONFIG_IPTYPE_STRING = "sip_config_iptype_string"; + + /** + * Local IPaddress used for SIP signaling. + */ + public static final String KEY_SIP_CONFIG_UE_DEFAULT_IPADDRESS_STRING = + "sip_config_ue_default_ipaddress_string"; + + /** + * Local port used for sending SIP traffic + */ + public static final String KEY_SIP_CONFIG_UE_DEFAULT_PORT_INT = + "sip_config_ue_default_port_int"; + + /** + * SIP server / PCSCF default ip address + */ + public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_IPADDRESS_STRING = + "sip_config_server_default_ipaddress_string"; + + /** + * SIP server / PCSCF port used for sending SIP traffic + */ + public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_PORT_INT = + "sip_config_server_default_port_int"; + + /** + * Flag specifying if Network Address Translation is enabled and UE is behind a NAT. + */ + public static final String KEY_SIP_CONFIG_IS_NAT_ENABLED_BOOL = + "sip_config_is_nat_enabled_bool"; + + /** + * UE's public IPaddress when UE is behind a NAT. + * <p> + * This key will not exist if {@link #KEY_SIP_CONFIG_IS_NAT_ENABLED_BOOL} is {@code false}. + */ + public static final String KEY_SIP_CONFIG_UE_PUBLIC_IPADDRESS_WITH_NAT_STRING = + "sip_config_ue_public_ipaddress_with_nat_string"; + + /** + * UE's public SIP port when UE is behind a NAT. + * <p> + * This key will not exist if {@link #KEY_SIP_CONFIG_IS_NAT_ENABLED_BOOL} is {@code false}. + */ + public static final String KEY_SIP_CONFIG_UE_PUBLIC_PORT_WITH_NAT_INT = + "sip_config_ue_public_port_with_nat_int"; + + /** + * Flag specifying if Globally routable user-agent uri (GRUU) is enabled as per TS 23.808 + */ + public static final String KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL = + "sip_config_is_gruu_enabled_bool"; + + /** + * UE's Globally routable user-agent uri if this feature is enabled. + * <p> + * This key will not exist if {@link #KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL} is {@code false}. + */ + public static final String KEY_SIP_CONFIG_UE_PUBLIC_GRUU_STRING = + "sip_config_ue_public_gruu_string"; + + /** + * Flag specifying if SIP over IPSec is enabled. + */ + public static final String KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL = + "sip_config_is_ipsec_enabled_bool"; + /** + * UE's SIP port used to send traffic when IPSec is enabled. + * <p> + * This key will not exist if {@link #KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL} is {@code false}. + */ + public static final String KEY_SIP_CONFIG_UE_IPSEC_CLIENT_PORT_INT = + "sip_config_ue_ipsec_client_port_int"; + + /** + * UE's SIP port used to receive traffic when IPSec is enabled. + * <p> + * This key will not exist if {@link #KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL} is {@code false}. + */ + public static final String KEY_SIP_CONFIG_UE_IPSEC_SERVER_PORT_INT = + "sip_config_ue_ipsec_server_port_int"; + + /** + * UE's SIP port used for the previous IPsec security association if IPSec is enabled. + * <p> + * This key will not exist if {@link #KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL} is {@code false}. + */ + public static final String KEY_SIP_CONFIG_UE_IPSEC_OLD_CLIENT_PORT_INT = + "sip_config_ue_ipsec_old_client_port_int"; + + /** + * Port number used by the SIP server to send SIP traffic when IPSec is enabled. + * <p> + * This key will not exist if {@link #KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL} is {@code false}. + */ + public static final String KEY_SIP_CONFIG_SERVER_IPSEC_CLIENT_PORT_INT = + "sip_config_server_ipsec_client_port_int"; + + /** + * Port number used by the SIP server to receive incoming SIP traffic when IPSec is enabled. + * <p> + * This key will not exist if {@link #KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL} is {@code false}. + */ + public static final String KEY_SIP_CONFIG_SERVER_IPSEC_SERVER_PORT_INT = + "sip_config_server_ipsec_server_port_int"; + + /** + * Port number used by the SIP server to send SIP traffic on the previous IPSec security + * association when IPSec is enabled. + * <p> + * This key will not exist if {@link #KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL} is {@code false}. + */ + public static final String KEY_SIP_CONFIG_SERVER_IPSEC_OLD_CLIENT_PORT_INT = + "sip_config_server_ipsec_old_client_port_int"; + /** + * SIP Authentication header string + */ + public static final String KEY_SIP_CONFIG_AUTHENTICATION_HEADER_STRING = + "sip_config_auhentication_header_string"; + + /** + * SIP Authentication nonce string + */ + public static final String KEY_SIP_CONFIG_AUTHENTICATION_NONCE_STRING = + "sip_config_authentication_nonce_string"; + + /** + * SIP service route header string + */ + public static final String KEY_SIP_CONFIG_SERVICE_ROUTE_HEADER_STRING = + "sip_config_service_route_header_string"; + + /** + * SIP security verify header string + */ + public static final String KEY_SIP_CONFIG_SECURITY_VERIFY_HEADER_STRING = + "sip_config_security_verify_header_string"; + + /** + * SIP Path header string + */ + public static final String KEY_SIP_CONFIG_PATH_HEADER_STRING = + "sip_config_path_header_string"; + + /** + * SIP User part string in contact header + */ + public static final String KEY_SIP_CONFIG_URI_USER_PART_STRING = + "sip_config_uri_user_part_string"; + + /** + * SIP P-access-network-info header string + */ + public static final String KEY_SIP_CONFIG_P_ACCESS_NETWORK_INFO_HEADER_STRING = + "sip_config_p_access_network_info_header_string"; + + /** + * SIP P-last-access-network-info header string + */ + public static final String KEY_SIP_CONFIG_P_LAST_ACCESS_NETWORK_INFO_HEADER_STRING = + "sip_config_p_last_access_network_info_header_string"; + + /** + * SIP P-associated-uri header string + */ + public static final String KEY_SIP_CONFIG_P_ASSOCIATED_URI_HEADER_STRING = + "sip_config_p_associated_uri_header_string"; + + /**@hide*/ + @StringDef(prefix = "KEY_SIP_CONFIG", suffix = "_STRING", value = { + KEY_SIP_CONFIG_TRANSPORT_TYPE_STRING, + KEY_SIP_CONFIG_UE_PUBLIC_USER_ID_STRING, + KEY_SIP_CONFIG_UE_PRIVATE_USER_ID_STRING, + KEY_SIP_CONFIG_HOME_DOMAIN_STRING, + KEY_SIP_CONFIG_IMEI_STRING, + KEY_SIP_CONFIG_IPTYPE_STRING, + KEY_SIP_CONFIG_UE_DEFAULT_IPADDRESS_STRING, + KEY_SIP_CONFIG_SERVER_DEFAULT_IPADDRESS_STRING, + KEY_SIP_CONFIG_UE_PUBLIC_IPADDRESS_WITH_NAT_STRING, + KEY_SIP_CONFIG_UE_PUBLIC_GRUU_STRING, + KEY_SIP_CONFIG_AUTHENTICATION_HEADER_STRING, + KEY_SIP_CONFIG_AUTHENTICATION_NONCE_STRING, + KEY_SIP_CONFIG_SERVICE_ROUTE_HEADER_STRING, + KEY_SIP_CONFIG_SECURITY_VERIFY_HEADER_STRING, + KEY_SIP_CONFIG_PATH_HEADER_STRING, + KEY_SIP_CONFIG_URI_USER_PART_STRING, + KEY_SIP_CONFIG_P_ACCESS_NETWORK_INFO_HEADER_STRING, + KEY_SIP_CONFIG_P_LAST_ACCESS_NETWORK_INFO_HEADER_STRING, + KEY_SIP_CONFIG_P_ASSOCIATED_URI_HEADER_STRING + }) + @Retention(RetentionPolicy.SOURCE) + public @interface StringConfigKey {} + + /**@hide*/ + @StringDef(prefix = "KEY_SIP_CONFIG", suffix = "_INT", value = { + KEY_SIP_CONFIG_MAX_PAYLOAD_SIZE_ON_UDP_INT, + KEY_SIP_CONFIG_UE_DEFAULT_PORT_INT, + KEY_SIP_CONFIG_SERVER_DEFAULT_PORT_INT, + KEY_SIP_CONFIG_UE_PUBLIC_PORT_WITH_NAT_INT, + KEY_SIP_CONFIG_UE_IPSEC_CLIENT_PORT_INT, + KEY_SIP_CONFIG_UE_IPSEC_SERVER_PORT_INT, + KEY_SIP_CONFIG_UE_IPSEC_OLD_CLIENT_PORT_INT, + KEY_SIP_CONFIG_SERVER_IPSEC_CLIENT_PORT_INT, + KEY_SIP_CONFIG_SERVER_IPSEC_SERVER_PORT_INT, + KEY_SIP_CONFIG_SERVER_IPSEC_OLD_CLIENT_PORT_INT + }) + @Retention(RetentionPolicy.SOURCE) + public @interface IntConfigKey {} + + /**@hide*/ + @StringDef(prefix = "KEY_SIP_CONFIG", suffix = "_BOOL", value = { + KEY_SIP_CONFIG_IS_COMPACT_FORM_ENABLED_BOOL, + KEY_SIP_CONFIG_IS_KEEPALIVE_ENABLED_BOOL, + KEY_SIP_CONFIG_IS_NAT_ENABLED_BOOL, + KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL, + KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL + }) + @Retention(RetentionPolicy.SOURCE) + public @interface BooleanConfigKey {} + + /** + * Builder class to be used when constructing a new SipDelegateImsConfiguration. + */ + public static class Builder { + private final long mVersion; + private final PersistableBundle mBundle; + + /** + * Creates an empty implementation of SipDelegateImsConfiguration. + * @param version The version associated with the SipDelegateImsConfiguration being built. + * See {@link #getVersion} for more information. + */ + public Builder(int version) { + mVersion = version; + mBundle = new PersistableBundle(); + } + /** + * Clones an existing implementation of SipDelegateImsConfiguration to handle situations + * where only a small number of parameters have changed from the previous configuration. + * <p> + * Automatically increments the version of this configuration by 1. See {@link #getVersion} + * for more information. + */ + public Builder(@NonNull SipDelegateImsConfiguration config) { + mVersion = config.getVersion() + 1; + mBundle = config.copyBundle(); + } + /** + * Put a string value into this configuration bundle for the given key. + */ + public Builder putString(@StringConfigKey String key, String value) { + mBundle.putString(key, value); + return this; + } + + /** + * Replace the existing default value with a new value for a given key. + */ + public Builder putInt(@IntConfigKey String key, int value) { + mBundle.putInt(key, value); + return this; + } + + /** + * Replace the existing default value with a new value for a given key. + */ + public Builder putBoolean(@BooleanConfigKey String key, boolean value) { + mBundle.putBoolean(key, value); + return this; + } + + /** + * @return a new SipDelegateImsConfiguration from this Builder. + */ + public SipDelegateImsConfiguration build() { + return new SipDelegateImsConfiguration(mVersion, mBundle); + } + } + + private final long mVersion; + private final PersistableBundle mBundle; + + private SipDelegateImsConfiguration(long version, PersistableBundle bundle) { + mVersion = version; + mBundle = bundle; + } + + private SipDelegateImsConfiguration(Parcel source) { + mVersion = source.readLong(); + mBundle = source.readPersistableBundle(); + } + + /** + * @return the string value associated with a given key or {@code null} if it doesn't exist. + */ + public @StringConfigKey String getString(String key) { + return mBundle.getString(key); + } + + /** + * @return the Integer value associated with a given key or {@code null} if the value doesn't + * exist. + */ + public @IntConfigKey Integer getInt(String key) { + if (!mBundle.containsKey(key)) { + return null; + } + return mBundle.getInt(key); + } + + /** + * @return the Integer value associated with a given key or {@code null} if the value doesn't + * exist. + */ + public @BooleanConfigKey Boolean getBoolen(String key) { + if (!mBundle.containsKey(key)) { + return null; + } + return mBundle.getBoolean(key); + } + + /** + * @return a shallow copy of the full configuration. + */ + public PersistableBundle copyBundle() { + return new PersistableBundle(mBundle); + } + + /** + * An integer representing the version number of this SipDelegateImsConfiguration. + * {@link SipMessage}s that are created using this configuration will also have a this + * version number associated with them, which will allow the IMS service to validate that the + * {@link SipMessage} was using the latest configuration during creation and not a stale + * configuration due to race conditions between the configuration being updated and the RCS + * application not receiving the updated configuration before generating a new message. + * + * @return the version number associated with this {@link SipDelegateImsConfiguration}. + */ + public long getVersion() { + return mVersion; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeLong(mVersion); + dest.writePersistableBundle(mBundle); + } + + public static final Creator<SipDelegateImsConfiguration> CREATOR = + new Creator<SipDelegateImsConfiguration>() { + @Override + public SipDelegateImsConfiguration createFromParcel(Parcel source) { + return new SipDelegateImsConfiguration(source); + } + + @Override + public SipDelegateImsConfiguration[] newArray(int size) { + return new SipDelegateImsConfiguration[size]; + } + }; +} diff --git a/telephony/java/android/telephony/ims/SipDelegateManager.java b/telephony/java/android/telephony/ims/SipDelegateManager.java index 82c8a9cd58f4..337b7d49323d 100644 --- a/telephony/java/android/telephony/ims/SipDelegateManager.java +++ b/telephony/java/android/telephony/ims/SipDelegateManager.java @@ -17,29 +17,251 @@ package android.telephony.ims; import android.Manifest; +import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.content.Context; -import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceSpecificException; +import android.telephony.BinderCacheManager; import android.telephony.CarrierConfigManager; -import android.telephony.TelephonyFrameworkInitializer; import android.telephony.ims.aidl.IImsRcsController; +import android.telephony.ims.aidl.SipDelegateConnectionAidlWrapper; +import android.telephony.ims.stub.DelegateConnectionMessageCallback; +import android.telephony.ims.stub.DelegateConnectionStateCallback; +import android.telephony.ims.stub.SipDelegate; import com.android.internal.annotations.VisibleForTesting; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.concurrent.Executor; + /** - * Manages the creation and destruction of SipDelegates, which allow an IMS application to forward - * SIP messages for the purposes of providing a single IMS registration to the carrier's IMS network - * from multiple sources. + * Manages the creation and destruction of SipDelegates for the {@link ImsService} managing IMS + * for the subscription ID that this SipDelegateManager has been created for. + * + * This allows multiple IMS applications to forward SIP messages to/from their application for the + * purposes of providing a single IMS registration to the carrier's IMS network from potentially + * many IMS stacks implementing a subset of the supported MMTEL/RCS features. * @hide */ @SystemApi public class SipDelegateManager { + /** + * The SIP message has failed being sent or received for an unknown reason. + * <p> + * The caller should retry a message that failed with this response. + * @hide + */ + public static final int MESSAGE_FAILURE_REASON_UNKNOWN = 0; + + /** + * The remote service associated with this connection has died and the message was not + * properly sent/received. + * <p> + * This is considered a permanent error and the system will automatically begin the teardown and + * destruction of the SipDelegate. No further messages should be sent on this transport. + * @hide + */ + public static final int MESSAGE_FAILURE_REASON_DELEGATE_DEAD = 1; + + /** + * The message has not been sent/received because the delegate is in the process of closing and + * has become unavailable. No further messages should be sent/received on this delegate. + * @hide + */ + public static final int MESSAGE_FAILURE_REASON_DELEGATE_CLOSED = 2; + + /** + * The SIP message has an invalid start line and the message can not be sent. + * @hide + */ + public static final int MESSAGE_FAILURE_REASON_INVALID_START_LINE = 3; + + /** + * One or more of the header fields in the header section of the outgoing SIP message is invalid + * and the SIP message can not be sent. + * @hide + */ + public static final int MESSAGE_FAILURE_REASON_INVALID_HEADER_FIELDS = 4; + + /** + * The body content of the SIP message is invalid and the message can not be sent. + * @hide + */ + public static final int MESSAGE_FAILURE_REASON_INVALID_BODY_CONTENT = 5; + + /** + * The feature tag associated with the outgoing message does not match any known feature tags + * and this message can not be sent. + * @hide + */ + public static final int MESSAGE_FAILURE_REASON_INVALID_FEATURE_TAG = 6; + + /** + * The feature tag associated with the outgoing message is not enabled for the associated + * SipDelegateConnection and can not be sent. + * @hide + */ + public static final int MESSAGE_FAILURE_REASON_TAG_NOT_ENABLED_FOR_DELEGATE = 7; + + /** + * The link to the network has been lost and the outgoing message has failed to send. + * <p> + * This message should be retried when connectivity to the network is re-established. See + * {@link android.net.ConnectivityManager.NetworkCallback} for how this can be determined. + * @hide + */ + public static final int MESSAGE_FAILURE_REASON_NETWORK_NOT_AVAILABLE = 8; + + /** + * The outgoing SIP message has not been sent due to the SipDelegate not being registered for + * IMS at this time. + * <p> + * This is considered a temporary failure, the message should not be retried until an IMS + * registration change callback is received via + * {@link DelegateConnectionStateCallback#onFeatureTagStatusChanged} + * @hide + */ + public static final int MESSAGE_FAILURE_REASON_NOT_REGISTERED = 9; + + /** + * The outgoing SIP message has not been sent because the {@link SipDelegateImsConfiguration} + * version associated with the outgoing {@link SipMessage} is now stale and has failed + * validation checks. + * <p> + * The @link SipMessage} should be recreated using the newest + * {@link SipDelegateImsConfiguration} and sent again. + * @hide + */ + public static final int MESSAGE_FAILURE_REASON_STALE_IMS_CONFIGURATION = 10; + + /** + * The outgoing SIP message has not been sent because the internal state of the associated + * {@link SipDelegate} is changing and has temporarily brought the transport down. + * <p> + * This is considered a temporary error and the {@link SipDelegateConnection} should resend the + * message once {@link DelegateRegistrationState#DEREGISTERING_REASON_FEATURE_TAGS_CHANGING} is + * no longer reported. + * @hide + */ + public static final int MESSAGE_FAILURE_REASON_INTERNAL_DELEGATE_STATE_TRANSITION = 11; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "MESSAGE_FAILURE_REASON_", value = { + MESSAGE_FAILURE_REASON_UNKNOWN, + MESSAGE_FAILURE_REASON_DELEGATE_DEAD, + MESSAGE_FAILURE_REASON_DELEGATE_CLOSED, + MESSAGE_FAILURE_REASON_INVALID_START_LINE, + MESSAGE_FAILURE_REASON_INVALID_HEADER_FIELDS, + MESSAGE_FAILURE_REASON_INVALID_BODY_CONTENT, + MESSAGE_FAILURE_REASON_INVALID_FEATURE_TAG, + MESSAGE_FAILURE_REASON_TAG_NOT_ENABLED_FOR_DELEGATE, + MESSAGE_FAILURE_REASON_NETWORK_NOT_AVAILABLE, + MESSAGE_FAILURE_REASON_NOT_REGISTERED, + MESSAGE_FAILURE_REASON_STALE_IMS_CONFIGURATION, + MESSAGE_FAILURE_REASON_INTERNAL_DELEGATE_STATE_TRANSITION + }) + public @interface MessageFailureReason {} + + + /** + * Access to use this feature tag has been denied for an unknown reason. + * @hide + */ + public static final int DENIED_REASON_UNKNOWN = 0; + + /** + * This feature tag is allowed to be used by this SipDelegateConnection, but it is in use by + * another SipDelegateConnection and can not be associated with this delegate. The feature tag + * will stay in this state until the feature tag is release by the other application. + * @hide + */ + public static final int DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE = 1; + + /** + * Access to use this feature tag has been denied because this application does not have the + * permissions required to access this feature tag. + * @hide + */ + public static final int DENIED_REASON_NOT_ALLOWED = 2; + + /** + * Access to use this feature tag has been denied because single registration is not allowed by + * the carrier at this time. The application should fall back to dual registration if + * applicable. + * @hide + */ + public static final int DENIED_REASON_SINGLE_REGISTRATION_NOT_ALLOWED = 3; + + /** + * This feature tag is not recognized as a valid feature tag by the SipDelegate and has been + * denied. + * @hide + */ + public static final int DENIED_REASON_INVALID = 4; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "DENIED_REASON_", value = { + DENIED_REASON_UNKNOWN, + DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE, + DENIED_REASON_NOT_ALLOWED, + DENIED_REASON_SINGLE_REGISTRATION_NOT_ALLOWED, + DENIED_REASON_INVALID + }) + public @interface DeniedReason {} + + /** + * The SipDelegate has closed due to an unknown reason. + * @hide + */ + public static final int SIP_DELEGATE_DESTROY_REASON_UNKNOWN = 0; + + /** + * The SipDelegate has closed because the IMS service has died unexpectedly. + * @hide + */ + public static final int SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD = 1; + + /** + * The SipDelegate has closed because the IMS application has requested that the connection be + * destroyed. + * @hide + */ + public static final int SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP = 2; + + /** + * The SipDelegate has closed because the IMS service does not support the creation of + * SipDelegates. + * @hide + */ + public static final int SIP_DELEGATE_DESTROY_REASON_SERVICE_NOT_SUPPORTED = 3; + + /** + * The SipDelegate has been closed due to the user disabling RCS. + * @hide + */ + public static final int SIP_DELEGATE_DESTROY_REASON_USER_DISABLED_RCS = 4; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "SIP_DELEGATE_DESTROY_REASON", value = { + SIP_DELEGATE_DESTROY_REASON_UNKNOWN, + SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD, + SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP, + SIP_DELEGATE_DESTROY_REASON_SERVICE_NOT_SUPPORTED, + SIP_DELEGATE_DESTROY_REASON_USER_DISABLED_RCS + }) + public @interface SipDelegateDestroyReason {} + private final Context mContext; private final int mSubId; + private final BinderCacheManager<IImsRcsController> mBinderCache; /** * Only visible for testing. To instantiate an instance of this class, please use @@ -47,9 +269,11 @@ public class SipDelegateManager { * @hide */ @VisibleForTesting - public SipDelegateManager(Context context, int subId) { + public SipDelegateManager(Context context, int subId, + BinderCacheManager<IImsRcsController> binderCache) { mContext = context; mSubId = subId; + mBinderCache = binderCache; } /** @@ -69,7 +293,7 @@ public class SipDelegateManager { @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isSupported() throws ImsException { try { - IImsRcsController controller = getIImsRcsController(); + IImsRcsController controller = mBinderCache.getBinder(); if (controller == null) { throw new ImsException("Telephony server is down", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); @@ -83,11 +307,90 @@ public class SipDelegateManager { } } - private IImsRcsController getIImsRcsController() { - IBinder binder = TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getTelephonyImsServiceRegisterer() - .get(); - return IImsRcsController.Stub.asInterface(binder); + /** + * Request that the ImsService implementation create a SipDelegate, which will configure the + * ImsService to forward SIP traffic that matches the filtering criteria set in supplied + * {@link DelegateRequest} to the application that the supplied callbacks are registered for. + * <p> + * This API requires that the caller is running as part of a long-running process and will + * always be available to handle incoming messages. One mechanism that can be used for this is + * the {@link android.service.carrier.CarrierMessagingClientService}, which the framework keeps + * a persistent binding to when the app is the default SMS application. + * @param request The parameters that are associated with the SipDelegate creation request that + * will be used to create the SipDelegate connection. + * @param executor The executor that will be used to call the callbacks associated with this + * SipDelegate. + * @param dc The callback that will be used to notify the listener of the creation/destruction + * of the remote SipDelegate as well as changes to the state of the remote SipDelegate + * connection. + * @param mc The callback that will be used to notify the listener of new incoming SIP messages + * as well as the status of messages that were sent by the associated + * SipDelegateConnection. + * @throws ImsException Thrown if there was a problem communicating with the ImsService + * associated with this SipDelegateManager. See {@link ImsException#getCode()}. + * @hide + */ + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + public void createSipDelegate(@NonNull DelegateRequest request, @NonNull Executor executor, + @NonNull DelegateConnectionStateCallback dc, + @NonNull DelegateConnectionMessageCallback mc) throws ImsException { + if (request == null || executor == null || dc == null || mc == null) { + throw new IllegalArgumentException("Invalid arguments passed into createSipDelegate"); + } + try { + SipDelegateConnectionAidlWrapper wrapper = + new SipDelegateConnectionAidlWrapper(executor, dc, mc); + IImsRcsController controller = mBinderCache.listenOnBinder(wrapper, + wrapper::binderDied); + if (controller == null) { + throw new ImsException("Telephony server is down", + ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + controller.createSipDelegate(mSubId, request, wrapper.getStateCallbackBinder(), + wrapper.getMessageCallbackBinder()); + } catch (ServiceSpecificException e) { + throw new ImsException(e.getMessage(), e.errorCode); + } catch (RemoteException e) { + throw new ImsException(e.getMessage(), + ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + } + + /** + * Destroy a previously created {@link SipDelegateConnection} that was created using + * {@link #createSipDelegate}. + * <p> + * This will also clean up all related callbacks in the associated ImsService. + * @param delegateConnection The SipDelegateConnection to destroy. + * @param reason The reason for why this SipDelegateConnection was destroyed. + * @hide + */ + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + public void destroySipDelegate(@NonNull SipDelegateConnection delegateConnection, + @SipDelegateDestroyReason int reason) { + + if (delegateConnection == null) { + throw new IllegalArgumentException("invalid argument passed into destroySipDelegate"); + } + if (delegateConnection instanceof SipDelegateConnectionAidlWrapper) { + SipDelegateConnectionAidlWrapper w = + (SipDelegateConnectionAidlWrapper) delegateConnection; + try { + IImsRcsController c = mBinderCache.removeRunnable(w); + c.destroySipDelegate(mSubId, w.getSipDelegateBinder(), reason); + } catch (RemoteException e) { + // Connection to telephony died, but this will signal destruction of SipDelegate + // eventually anyway, so return normally. + try { + w.getStateCallbackBinder().onDestroyed( + SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP); + } catch (RemoteException ignore) { + // Local to process. + } + } + } else { + throw new IllegalArgumentException("Unknown SipDelegateConnection implementation passed" + + " into this method"); + } } } diff --git a/telephony/java/android/telephony/ims/SipMessage.aidl b/telephony/java/android/telephony/ims/SipMessage.aidl new file mode 100644 index 000000000000..5140f8ac357f --- /dev/null +++ b/telephony/java/android/telephony/ims/SipMessage.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims; + +parcelable SipMessage; diff --git a/telephony/java/android/telephony/ims/SipMessage.java b/telephony/java/android/telephony/ims/SipMessage.java new file mode 100644 index 000000000000..c3b1be2d7fc8 --- /dev/null +++ b/telephony/java/android/telephony/ims/SipMessage.java @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims; + +import android.annotation.NonNull; +import android.os.Build; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Represents a partially encoded SIP message. See RFC 3261 for more information on how SIP + * messages are structured and used. + * <p> + * The SIP message is represented in a partially encoded form in order to allow for easier + * verification and should not be used as a generic SIP message container. + * @hide + */ +public final class SipMessage implements Parcelable { + // Should not be set to true for production! + private static final boolean IS_DEBUGGING = Build.IS_ENG; + + private static final String[] SIP_REQUEST_METHODS = new String[] {"INVITE", "ACK", "OPTIONS", + "BYE", "CANCEL", "REGISTER"}; + + private final String mStartLine; + private final String mHeaderSection; + private final byte[] mContent; + + /** + * Represents a partially encoded SIP message. + * + * @param startLine The start line of the message, containing either the request-line or + * status-line. + * @param headerSection A String containing the full unencoded SIP message header. + * @param content UTF-8 encoded SIP message body. + */ + public SipMessage(@NonNull String startLine, @NonNull String headerSection, + @NonNull byte[] content) { + if (startLine == null || headerSection == null || content == null) { + throw new IllegalArgumentException("One or more null parameters entered"); + } + mStartLine = startLine; + mHeaderSection = headerSection; + mContent = content; + } + + /** + * Private constructor used only for unparcelling. + */ + private SipMessage(Parcel source) { + mStartLine = source.readString(); + mHeaderSection = source.readString(); + mContent = new byte[source.readInt()]; + source.readByteArray(mContent); + } + /** + * @return The start line of the SIP message, which contains either the request-line or + * status-line. + */ + public @NonNull String getStartLine() { + return mStartLine; + } + + /** + * @return The full, unencoded header section of the SIP message. + */ + public @NonNull String getHeaderSection() { + return mHeaderSection; + } + + /** + * @return only the UTF-8 encoded SIP message body. + */ + public @NonNull byte[] getContent() { + return mContent; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mStartLine); + dest.writeString(mHeaderSection); + dest.writeInt(mContent.length); + dest.writeByteArray(mContent); + } + + public static final Creator<SipMessage> CREATOR = new Creator<SipMessage>() { + @Override + public SipMessage createFromParcel(Parcel source) { + return new SipMessage(source); + } + + @Override + public SipMessage[] newArray(int size) { + return new SipMessage[size]; + } + }; + + @Override + public String toString() { + StringBuilder b = new StringBuilder(); + b.append("StartLine: ["); + if (IS_DEBUGGING) { + b.append(mStartLine); + } else { + b.append(sanitizeStartLineRequest(mStartLine)); + } + b.append("], ["); + b.append("Header: ["); + if (IS_DEBUGGING) { + b.append(mHeaderSection); + } else { + // only identify transaction id/call ID when it is available. + b.append("***"); + } + b.append("], "); + b.append("Content: [NOT SHOWN]"); + return b.toString(); + } + + /** + * Start lines containing requests are formatted: METHOD SP Request-URI SP SIP-Version CRLF. + * Detect if this is a REQUEST and redact Request-URI portion here, as it contains PII. + */ + private String sanitizeStartLineRequest(String startLine) { + String[] splitLine = startLine.split(" "); + if (splitLine == null || splitLine.length == 0) { + return "(INVALID STARTLINE)"; + } + for (String method : SIP_REQUEST_METHODS) { + if (splitLine[0].contains(method)) { + return splitLine[0] + " <Request-URI> " + splitLine[2]; + } + } + return startLine; + } +} diff --git a/telephony/java/android/telephony/ims/aidl/IImsMmTelFeature.aidl b/telephony/java/android/telephony/ims/aidl/IImsMmTelFeature.aidl index b9a6b3c38a92..37fec7a73634 100644 --- a/telephony/java/android/telephony/ims/aidl/IImsMmTelFeature.aidl +++ b/telephony/java/android/telephony/ims/aidl/IImsMmTelFeature.aidl @@ -21,6 +21,7 @@ import android.telephony.ims.aidl.IImsMmTelListener; import android.telephony.ims.aidl.IImsSmsListener; import android.telephony.ims.aidl.IImsCapabilityCallback; import android.telephony.ims.feature.CapabilityChangeRequest; +import android.telephony.ims.RtpHeaderExtensionType; import android.telephony.ims.ImsCallProfile; import com.android.ims.internal.IImsCallSession; @@ -29,6 +30,8 @@ import com.android.ims.internal.IImsMultiEndpoint; import com.android.ims.internal.IImsRegistrationListener; import com.android.ims.internal.IImsUt; +import java.util.List; + /** * See MmTelFeature for more information. * {@hide} @@ -37,6 +40,7 @@ interface IImsMmTelFeature { void setListener(IImsMmTelListener l); int getFeatureState(); ImsCallProfile createCallProfile(int callSessionType, int callType); + void changeOfferedRtpHeaderExtensionTypes(in List<RtpHeaderExtensionType> types); IImsCallSession createCallSession(in ImsCallProfile profile); int shouldProcessCall(in String[] uris); IImsUt getUtInterface(); diff --git a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl index 8e84e9373f65..f218e35a5a9b 100644 --- a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl +++ b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl @@ -17,10 +17,15 @@ package android.telephony.ims.aidl; import android.net.Uri; +import android.telephony.ims.DelegateRequest; import android.telephony.ims.aidl.IImsCapabilityCallback; +import android.telephony.ims.aidl.IImsRegistrationCallback; import android.telephony.ims.aidl.IRcsUceControllerCallback; import android.telephony.ims.aidl.IRcsUcePublishStateCallback; -import android.telephony.ims.aidl.IImsRegistrationCallback; +import android.telephony.ims.aidl.IRcsUcePublishStateCallback; +import android.telephony.ims.aidl.ISipDelegate; +import android.telephony.ims.aidl.ISipDelegateMessageCallback; +import android.telephony.ims.aidl.ISipDelegateConnectionStateCallback; import com.android.ims.ImsFeatureContainer; import com.android.ims.internal.IImsServiceFeatureCallback; @@ -58,6 +63,10 @@ interface IImsRcsController { // SipDelegateManager boolean isSipDelegateSupported(int subId); + void createSipDelegate(int subId, in DelegateRequest request, + ISipDelegateConnectionStateCallback delegateState, + ISipDelegateMessageCallback delegateMessage); + void destroySipDelegate(int subId, ISipDelegate connection, int reason); // Internal commands that should not be made public void registerRcsFeatureCallback(int slotId, in IImsServiceFeatureCallback callback); diff --git a/telephony/java/android/telephony/ims/aidl/ISipDelegate.aidl b/telephony/java/android/telephony/ims/aidl/ISipDelegate.aidl new file mode 100644 index 000000000000..477ee958e1e8 --- /dev/null +++ b/telephony/java/android/telephony/ims/aidl/ISipDelegate.aidl @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims.aidl; + +import android.telephony.ims.SipMessage; + +/** + * See {@link SipDelegate} and {@link SipDelegateConnection} for docs regarding this callback. + * {@hide} + */ +oneway interface ISipDelegate { + void sendMessage(in SipMessage sipMessage, int configVersion); + void notifyMessageReceived(in String viaTransactionId); + void notifyMessageReceiveError(in String viaTransactionId, int reason); + + // only used by SipDelegate. + void closeDialog(in String callId); +} diff --git a/telephony/java/android/telephony/ims/aidl/ISipDelegateConnectionStateCallback.aidl b/telephony/java/android/telephony/ims/aidl/ISipDelegateConnectionStateCallback.aidl new file mode 100644 index 000000000000..ddfcb9994cb8 --- /dev/null +++ b/telephony/java/android/telephony/ims/aidl/ISipDelegateConnectionStateCallback.aidl @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims.aidl; + +import android.telephony.ims.DelegateRegistrationState; +import android.telephony.ims.FeatureTagState; +import android.telephony.ims.SipDelegateImsConfiguration; +import android.telephony.ims.aidl.ISipDelegate; + +/** + * See {@link SipDelegateConnectionStateCallback} for docs regarding this callback. + * {@hide} + */ +oneway interface ISipDelegateConnectionStateCallback { + void onCreated(ISipDelegate c); + void onFeatureTagStatusChanged(in DelegateRegistrationState registrationState, + in List<FeatureTagState> deniedFeatureTags); + void onImsConfigurationChanged(in SipDelegateImsConfiguration registeredSipConfig); + void onDestroyed(int reason); +} diff --git a/telephony/java/android/telephony/ims/aidl/ISipDelegateMessageCallback.aidl b/telephony/java/android/telephony/ims/aidl/ISipDelegateMessageCallback.aidl new file mode 100644 index 000000000000..30b7d6c70647 --- /dev/null +++ b/telephony/java/android/telephony/ims/aidl/ISipDelegateMessageCallback.aidl @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims.aidl; + +import android.telephony.ims.SipMessage; + +/** + * See {@link DelegateMessageCallback} and {@link DelegateConnectionMessageCallback} for docs + * regarding this callback. + * {@hide} + */ +oneway interface ISipDelegateMessageCallback { + void onMessageReceived(in SipMessage message); + void onMessageSent(in String viaTransactionId); + void onMessageSendFailure(in String viaTransactionId, int reason); +} diff --git a/telephony/java/android/telephony/ims/aidl/ISipDelegateStateCallback.aidl b/telephony/java/android/telephony/ims/aidl/ISipDelegateStateCallback.aidl new file mode 100644 index 000000000000..609ee260cbab --- /dev/null +++ b/telephony/java/android/telephony/ims/aidl/ISipDelegateStateCallback.aidl @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims.aidl; + +import android.telephony.ims.DelegateRegistrationState; +import android.telephony.ims.FeatureTagState; +import android.telephony.ims.SipDelegateImsConfiguration; +import android.telephony.ims.aidl.ISipDelegate; + +/** + * See {@link SipDelegateStateCallback} for docs regarding this callback. + * {@hide} + */ +oneway interface ISipDelegateStateCallback { + void onCreated(ISipDelegate c, in List<FeatureTagState> deniedFeatureTags); + void onFeatureTagRegistrationChanged(in DelegateRegistrationState registrationState); + void onImsConfigurationChanged(in SipDelegateImsConfiguration registeredSipConfig); + void onDestroyed(int reason); +} diff --git a/telephony/java/android/telephony/ims/aidl/ISipTransport.aidl b/telephony/java/android/telephony/ims/aidl/ISipTransport.aidl index fe233430ffad..cd888391c584 100644 --- a/telephony/java/android/telephony/ims/aidl/ISipTransport.aidl +++ b/telephony/java/android/telephony/ims/aidl/ISipTransport.aidl @@ -16,9 +16,17 @@ package android.telephony.ims.aidl; +import android.telephony.ims.DelegateRequest; +import android.telephony.ims.aidl.ISipDelegate; +import android.telephony.ims.aidl.ISipDelegateMessageCallback; +import android.telephony.ims.aidl.ISipDelegateStateCallback; + /** * Interface for commands to the SIP Transport implementation. * {@hide} */ -interface ISipTransport { +oneway interface ISipTransport { + void createSipDelegate(in DelegateRequest request, ISipDelegateStateCallback dc, + ISipDelegateMessageCallback mc); + void destroySipDelegate(ISipDelegate delegate, int reason); } diff --git a/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java new file mode 100644 index 000000000000..a7f62cc32be1 --- /dev/null +++ b/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims.aidl; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Binder; +import android.os.RemoteException; +import android.telephony.ims.DelegateMessageCallback; +import android.telephony.ims.DelegateRegistrationState; +import android.telephony.ims.DelegateStateCallback; +import android.telephony.ims.FeatureTagState; +import android.telephony.ims.SipDelegateImsConfiguration; +import android.telephony.ims.SipDelegateManager; +import android.telephony.ims.SipMessage; +import android.telephony.ims.stub.SipDelegate; + +import java.util.List; +import java.util.concurrent.Executor; + +/** + * Implementation of callbacks by wrapping the internal AIDL from telephony. Also implements + * ISipDelegate internally when {@link DelegateStateCallback#onCreated(SipDelegate, List)} is called + * in order to trampoline events back to telephony. + * @hide + */ +public class SipDelegateAidlWrapper implements DelegateStateCallback, DelegateMessageCallback { + + private final ISipDelegate.Stub mDelegateBinder = new ISipDelegate.Stub() { + @Override + public void sendMessage(SipMessage sipMessage, int configVersion) { + SipDelegate d = mDelegate; + final long token = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> d.sendMessage(sipMessage, configVersion)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void notifyMessageReceived(String viaTransactionId) { + SipDelegate d = mDelegate; + final long token = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> d.notifyMessageReceived(viaTransactionId)); + } finally { + Binder.restoreCallingIdentity(token); + } + + } + + @Override + public void notifyMessageReceiveError(String viaTransactionId, int reason) { + SipDelegate d = mDelegate; + final long token = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> d.notifyMessageReceiveError(viaTransactionId, reason)); + } finally { + Binder.restoreCallingIdentity(token); + } + + } + + @Override + public void closeDialog(String callId) { + SipDelegate d = mDelegate; + final long token = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> d.closeDialog(callId)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + }; + + private final ISipDelegateMessageCallback mMessageBinder; + private final ISipDelegateStateCallback mStateBinder; + private final Executor mExecutor; + + private volatile SipDelegate mDelegate; + + public SipDelegateAidlWrapper(Executor executor, ISipDelegateStateCallback stateBinder, + ISipDelegateMessageCallback messageBinder) { + mExecutor = executor; + mStateBinder = stateBinder; + mMessageBinder = messageBinder; + } + + @Override + public void onMessageReceived(SipMessage message) { + try { + mMessageBinder.onMessageReceived(message); + } catch (RemoteException e) { + // BinderDied will be called on SipTransport instance to trigger destruction. Notify + // failure message failure locally for now. + SipDelegate d = mDelegate; + if (d != null) { + notifyLocalMessageFailedToBeReceived(message, + SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_DEAD); + } + } + } + + @Override + public void onMessageSent(String viaTransactionId) { + try { + mMessageBinder.onMessageSent(viaTransactionId); + } catch (RemoteException e) { + // BinderDied will trigger destroySipDelegate, so just ignore this locally. + } + } + + @Override + public void onMessageSendFailure(String viaTransactionId, int reason) { + try { + mMessageBinder.onMessageSendFailure(viaTransactionId, reason); + } catch (RemoteException e) { + // BinderDied will trigger destroySipDelegate, so just ignore this locally. + } + } + + @Override + public void onCreated(@NonNull SipDelegate delegate, + @Nullable List<FeatureTagState> deniedTags) { + mDelegate = delegate; + try { + mStateBinder.onCreated(mDelegateBinder, deniedTags); + } catch (RemoteException e) { + // BinderDied will trigger destroySipDelegate, so just ignore this locally. + } + } + + @Override + public void onFeatureTagRegistrationChanged(DelegateRegistrationState registrationState) { + try { + mStateBinder.onFeatureTagRegistrationChanged(registrationState); + } catch (RemoteException e) { + // BinderDied will trigger destroySipDelegate, so just ignore this locally. + } + } + + @Override + public void onImsConfigurationChanged(@NonNull SipDelegateImsConfiguration config) { + try { + mStateBinder.onImsConfigurationChanged(config); + } catch (RemoteException e) { + // BinderDied will trigger destroySipDelegate, so just ignore this locally. + } + } + + @Override + public void onDestroyed(int reasonCode) { + mDelegate = null; + try { + mStateBinder.onDestroyed(reasonCode); + } catch (RemoteException e) { + // Do not worry about this if the remote side is already dead. + } + } + + public SipDelegate getDelegate() { + return mDelegate; + } + + public ISipDelegate getDelegateBinder() { + return mDelegateBinder; + } + + private void notifyLocalMessageFailedToBeReceived(SipMessage m, int reason) { + //TODO: parse transaction ID or throw IllegalArgumentException if the SipMessage + // transaction ID can not be parsed. + SipDelegate d = mDelegate; + if (d != null) { + mExecutor.execute(() -> d.notifyMessageReceiveError(null, reason)); + } + } +} diff --git a/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java new file mode 100644 index 000000000000..3bd1a462b31a --- /dev/null +++ b/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java @@ -0,0 +1,260 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims.aidl; + +import android.os.Binder; +import android.os.IBinder; +import android.os.RemoteException; +import android.telephony.ims.DelegateRegistrationState; +import android.telephony.ims.FeatureTagState; +import android.telephony.ims.SipDelegateConnection; +import android.telephony.ims.SipDelegateImsConfiguration; +import android.telephony.ims.SipDelegateManager; +import android.telephony.ims.SipMessage; +import android.telephony.ims.stub.DelegateConnectionMessageCallback; +import android.telephony.ims.stub.DelegateConnectionStateCallback; +import android.telephony.ims.stub.SipDelegate; +import android.util.ArraySet; +import android.util.Log; + +import java.util.List; +import java.util.NoSuchElementException; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicReference; + +/** + * Wrapper class implementing {@link SipDelegateConnection} using AIDL, which is returned to the + * local process. Also holds a reference to incoming connection message and state AIDL impl to + * trampoline events to callbacks as well as notify the local process in the event that the remote + * process becomes unavailable. + * <p> + * When the remote {@link SipDelegate} is created, this instance tracks the + * {@link ISipDelegate} associated with it and implements the + * {@link SipDelegateConnection} sent back to the local callback. + * @hide + */ +public class SipDelegateConnectionAidlWrapper implements SipDelegateConnection, + IBinder.DeathRecipient { + private static final String LOG_TAG = "SipDelegateCAW"; + + private final ISipDelegateConnectionStateCallback.Stub mStateBinder = + new ISipDelegateConnectionStateCallback.Stub() { + @Override + public void onCreated(ISipDelegate c) { + associateSipDelegate(c); + final long token = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> + mStateCallback.onCreated(SipDelegateConnectionAidlWrapper.this)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void onFeatureTagStatusChanged(DelegateRegistrationState registrationState, + List<FeatureTagState> deniedFeatureTags) { + final long token = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> + mStateCallback.onFeatureTagStatusChanged(registrationState, + new ArraySet<>(deniedFeatureTags))); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void onImsConfigurationChanged(SipDelegateImsConfiguration registeredSipConfig) { + final long token = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> + mStateCallback.onImsConfigurationChanged(registeredSipConfig)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void onDestroyed(int reason) { + invalidateSipDelegateBinder(); + final long token = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> + mStateCallback.onDestroyed(reason)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + }; + + private final ISipDelegateMessageCallback.Stub mMessageBinder = + new ISipDelegateMessageCallback.Stub() { + @Override + public void onMessageReceived(SipMessage message) { + final long token = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> + mMessageCallback.onMessageReceived(message)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void onMessageSent(String viaTransactionId) { + final long token = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> + mMessageCallback.onMessageSent(viaTransactionId)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void onMessageSendFailure(String viaTransactionId, int reason) { + final long token = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> + mMessageCallback.onMessageSendFailure(viaTransactionId, reason)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + }; + + + private final Executor mExecutor; + private final DelegateConnectionStateCallback mStateCallback; + private final DelegateConnectionMessageCallback mMessageCallback; + private final AtomicReference<ISipDelegate> mDelegateBinder = + new AtomicReference<>(); + + /** + * Wrap the local state and message callbacks, calling the implementation of these interfaces + * when the remote process calls these methods. + */ + public SipDelegateConnectionAidlWrapper(Executor executor, + DelegateConnectionStateCallback stateCallback, + DelegateConnectionMessageCallback messageCallback) { + mExecutor = executor; + mStateCallback = stateCallback; + mMessageCallback = messageCallback; + } + + @Override + public void sendMessage(SipMessage sipMessage, int configVersion) { + try { + ISipDelegate conn = getSipDelegateBinder(); + if (conn == null) { + notifyLocalMessageFailedToSend(sipMessage, + SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_CLOSED); + return; + } + conn.sendMessage(sipMessage, configVersion); + } catch (RemoteException e) { + notifyLocalMessageFailedToSend(sipMessage, + SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_DEAD); + } + } + + @Override + public void notifyMessageReceived(String viaTransactionId) { + try { + ISipDelegate conn = getSipDelegateBinder(); + if (conn == null) { + return; + } + conn.notifyMessageReceived(viaTransactionId); + } catch (RemoteException e) { + // Nothing to do here, app will eventually get remote death callback. + } + } + + @Override + public void notifyMessageReceiveError(String viaTransactionId, int reason) { + try { + ISipDelegate conn = getSipDelegateBinder(); + if (conn == null) { + return; + } + conn.notifyMessageReceiveError(viaTransactionId, reason); + } catch (RemoteException e) { + // Nothing to do here, app will eventually get remote death callback. + } + } + + // Also called upon IImsRcsController death (telephony process dies). + @Override + public void binderDied() { + invalidateSipDelegateBinder(); + mExecutor.execute(() -> mStateCallback.onDestroyed( + SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD)); + } + + /** + * @return Implementation of state binder. + */ + public ISipDelegateConnectionStateCallback getStateCallbackBinder() { + return mStateBinder; + } + + /** + * @return Implementation of message binder. + */ + public ISipDelegateMessageCallback getMessageCallbackBinder() { + return mMessageBinder; + } + + /** + * @return The ISipDelegateConnection associated with this wrapper. + */ + public ISipDelegate getSipDelegateBinder() { + return mDelegateBinder.get(); + } + + private void associateSipDelegate(ISipDelegate c) { + if (c != null) { + try { + c.asBinder().linkToDeath(this, 0 /*flags*/); + } catch (RemoteException e) { + // already dead. + c = null; + } + } + mDelegateBinder.set(c); + } + + private void invalidateSipDelegateBinder() { + ISipDelegate oldVal = mDelegateBinder.getAndUpdate((unused) -> null); + if (oldVal != null) { + try { + oldVal.asBinder().unlinkToDeath(this, 0 /*flags*/); + } catch (NoSuchElementException e) { + Log.i(LOG_TAG, "invalidateSipDelegateBinder: " + e); + } + } + } + + private void notifyLocalMessageFailedToSend(SipMessage m, int reason) { + //TODO: parse transaction ID or throw IllegalArgumentException if the SipMessage + // transaction ID can not be parsed. + mExecutor.execute(() -> + mMessageCallback.onMessageSendFailure(null, reason)); + } +} diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java index d7b0e0f030ab..e570fb6f5612 100644 --- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java +++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java @@ -27,6 +27,8 @@ import android.telecom.TelecomManager; import android.telephony.ims.ImsCallProfile; import android.telephony.ims.ImsCallSession; import android.telephony.ims.ImsReasonInfo; +import android.telephony.ims.ImsService; +import android.telephony.ims.RtpHeaderExtensionType; import android.telephony.ims.aidl.IImsCapabilityCallback; import android.telephony.ims.aidl.IImsMmTelFeature; import android.telephony.ims.aidl.IImsMmTelListener; @@ -37,6 +39,7 @@ import android.telephony.ims.stub.ImsMultiEndpointImplBase; import android.telephony.ims.stub.ImsRegistrationImplBase; import android.telephony.ims.stub.ImsSmsImplBase; import android.telephony.ims.stub.ImsUtImplBase; +import android.util.ArraySet; import com.android.ims.internal.IImsCallSession; import com.android.ims.internal.IImsEcbm; @@ -45,6 +48,8 @@ import com.android.ims.internal.IImsUt; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.List; +import java.util.Set; /** * Base implementation for Voice and SMS (IR-92) and Video (IR-94) IMS support. @@ -93,6 +98,18 @@ public class MmTelFeature extends ImsFeature { } @Override + public void changeOfferedRtpHeaderExtensionTypes(List<RtpHeaderExtensionType> types) + throws RemoteException { + synchronized (mLock) { + try { + MmTelFeature.this.changeOfferedRtpHeaderExtensionTypes(new ArraySet<>(types)); + } catch (Exception e) { + throw new RemoteException(e.getMessage()); + } + } + } + + @Override public IImsCallSession createCallSession(ImsCallProfile profile) throws RemoteException { synchronized (mLock) { return createCallSessionInterface(profile); @@ -623,6 +640,24 @@ public class MmTelFeature extends ImsFeature { } /** + * Called by the framework to report a change to the RTP header extension types which should be + * offered during SDP negotiation (see RFC8285 for more information). + * <p> + * The {@link ImsService} should report the RTP header extensions which were accepted during + * SDP negotiation using {@link ImsCallProfile#setAcceptedRtpHeaderExtensionTypes(Set)}. + * + * @param extensionTypes The RTP header extensions the framework wishes to offer during + * outgoing and incoming call setup. An empty list indicates that there + * are no framework defined RTP header extension types to offer. + * @hide + */ + @SystemApi + public void changeOfferedRtpHeaderExtensionTypes( + @NonNull Set<RtpHeaderExtensionType> extensionTypes) { + // Base implementation - should be overridden if RTP header extension handling is supported. + } + + /** * @hide */ public IImsCallSession createCallSessionInterface(ImsCallProfile profile) diff --git a/telephony/java/android/telephony/ims/stub/DelegateConnectionMessageCallback.java b/telephony/java/android/telephony/ims/stub/DelegateConnectionMessageCallback.java new file mode 100644 index 000000000000..59f9601299b2 --- /dev/null +++ b/telephony/java/android/telephony/ims/stub/DelegateConnectionMessageCallback.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims.stub; + +import android.annotation.NonNull; +import android.telephony.ims.SipDelegateConnection; +import android.telephony.ims.SipDelegateManager; +import android.telephony.ims.SipMessage; + +/** + * The callback associated with a {@link SipDelegateConnection}, which handles newly received + * messages as well as the result of sending a SIP message. + * @hide + */ +public interface DelegateConnectionMessageCallback { + + /** + * A new {@link SipMessage} has been received from the delegate. + * @param message the {@link SipMessage} routed to this RCS application. + */ + void onMessageReceived(@NonNull SipMessage message); + + /** + * A message previously sent to the SIP delegate using + * {@link SipDelegateConnection#sendMessage} has been successfully sent. + * @param viaTransactionId The transaction ID found in the via header field of the + * previously sent {@link SipMessage}. + */ + void onMessageSent(@NonNull String viaTransactionId); + + /** + * A message previously sent to the SIP delegate using + * {@link SipDelegateConnection#sendMessage} has failed to be sent. + * @param viaTransactionId The Transaction ID found in the via header field of the + * previously sent {@link SipMessage}. + * @param reason The reason for the failure. + */ + void onMessageSendFailure(String viaTransactionId, + @SipDelegateManager.MessageFailureReason int reason); +} diff --git a/telephony/java/android/telephony/ims/stub/DelegateConnectionStateCallback.java b/telephony/java/android/telephony/ims/stub/DelegateConnectionStateCallback.java new file mode 100644 index 000000000000..976180538b18 --- /dev/null +++ b/telephony/java/android/telephony/ims/stub/DelegateConnectionStateCallback.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims.stub; + +import android.annotation.NonNull; +import android.telephony.ims.DelegateRegistrationState; +import android.telephony.ims.DelegateRequest; +import android.telephony.ims.FeatureTagState; +import android.telephony.ims.SipDelegateConnection; +import android.telephony.ims.SipDelegateImsConfiguration; +import android.telephony.ims.SipDelegateManager; + +import java.util.Set; + +/** + * The callback associated with a {@link SipDelegateConnection} that manages the state of the + * SipDelegateConnection. + * <p> + * After {@link SipDelegateManager#createSipDelegate} is used to request a new + * {@link SipDelegateConnection} be created, {@link #onCreated} will be called with the + * {@link SipDelegateConnection} instance that must be used to communicate with the remote + * {@link SipDelegate}. + * <p> + * After, {@link #onFeatureTagStatusChanged} will always be called at least once with the current + * status of the feature tags that have been requested. The application may receive multiple + * {@link #onFeatureTagStatusChanged} callbacks over the lifetime of the associated + * {@link SipDelegateConnection}, which will signal changes to how SIP messages associated with + * those feature tags will be handled. + * <p> + * In order to start sending SIP messages, the SIP configuration parameters will need to be + * received, so the messaging application should make no assumptions about these parameters and wait + * until {@link #onImsConfigurationChanged(SipDelegateImsConfiguration)} has been called. This is + * guaranteed to happen after the first {@link #onFeatureTagStatusChanged} if there is at least one + * feature tag that has been successfully associated with the {@link SipDelegateConnection}. If all + * feature tags were denied, no IMS configuration will be sent. + * <p> + * The {@link SipDelegateConnection} will stay associated with this RCS application until either the + * RCS application calls {@link SipDelegateManager#destroySipDelegate} or telephony destroys the + * {@link SipDelegateConnection}. In both cases, {@link #onDestroyed(int)} will be called. + * Telephony destroying the {@link SipDelegateConnection} instance is rare and will only happen in + * rare cases, such as if telephony itself or IMS service dies unexpectedly. See + * {@link SipDelegateManager.SipDelegateDestroyReason} reasons for more information on all of the + * cases that will trigger the {@link SipDelegateConnection} to be destroyed. + * + * @hide + */ +public interface DelegateConnectionStateCallback { + + /** + * A {@link SipDelegateConnection} has been successfully created for the + * {@link DelegateRequest} used when calling {@link SipDelegateManager#createSipDelegate}. + */ + void onCreated(@NonNull SipDelegateConnection c); + + /** + * The status of the RCS feature tags that were requested as part of the initial + * {@link DelegateRequest}. + * <p> + * There are four states that each RCS feature tag can be in: registered, deregistering, + * deregistered, and denied. + * <p> + * When a feature tag is considered registered, SIP messages associated with that feature tag + * may be sent and received freely. + * <p> + * When a feature tag is deregistering, the network IMS registration still contains the feature + * tag, however the IMS service and associated {@link SipDelegate} is in the progress of + * modifying the IMS registration to remove this feature tag and requires the application to + * perform an action before the IMS registration can change. The specific action required for + * the SipDelegate to continue modifying the IMS registration can be found in the definition of + * each {@link DelegateRegistrationState.DeregisteringReason}. + * <p> + * When a feature tag is in the deregistered state, new out-of-dialog SIP messages for that + * feature tag will be rejected, however due to network race conditions, the RCS application + * should still be able to handle new out-of-dialog SIP requests from the network. This may not + * be possible, however, if the IMS registration itself was lost. See the + * {@link DelegateRegistrationState.DeregisteredReason} reasons for more information on how SIP + * messages are handled in each of these cases. + * <p> + * If a feature tag is denied, no incoming messages will be routed to the associated + * {@link DelegateConnectionMessageCallback} and all outgoing SIP messages related to this + * feature tag will be rejected. See {@link SipDelegateManager.DeniedReason} + * reasons for more information about the conditions when this will happen. + * <p> + * The set of feature tags contained in the registered, deregistering, deregistered, and denied + * lists will always equal the set of feature tags requested in the initial + * {@link DelegateRequest}. + * <p> + * Transitions of feature tags from registered, deregistering, and deregistered and vice-versa + * may happen quite often, however transitions to/from denied are rare and only occur if the + * user has changed the role of your application to add/remove support for one or more requested + * feature tags or carrier provisioning has enabled or disabled single registration entirely. + * Please see {@link SipDelegateManager.DeniedReason} reasons for an explanation of each of + * these cases as well as what may cause them to change. + * + * @param registrationState The new IMS registration state of each of the feature tags + * associated with the {@link SipDelegate}. + * @param deniedFeatureTags A list of {@link FeatureTagState} objects, each containing a feature + * tag associated with this {@link SipDelegateConnection} that has no access to + * send/receive SIP messages as well as a reason for why the feature tag is denied. For more + * information on the reason why the feature tag was denied access, see the + * {@link SipDelegateManager.DeniedReason} reasons. + */ + void onFeatureTagStatusChanged(@NonNull DelegateRegistrationState registrationState, + @NonNull Set<FeatureTagState> deniedFeatureTags); + + + /** + * IMS configuration of the underlying IMS stack used by this IMS application for construction + * of the SIP messages that will be sent over the carrier's network. + * <p> + * There should never be assumptions made about the configuration of the underling IMS stack and + * the IMS application should wait for this indication before sending out any outgoing SIP + * messages. + * <p> + * Configuration may change due to IMS registration changes as well as + * other optional events on the carrier network. If IMS stack is already registered at the time + * of callback registration, then this method shall be invoked with the current configuration. + * Otherwise, there may be a delay in this method being called if initial IMS registration has + * not compleed yet. + * + * @param registeredSipConfig The configuration of the IMS stack registered on the IMS network. + */ + void onImsConfigurationChanged(@NonNull SipDelegateImsConfiguration registeredSipConfig); + + /** + * The previously created {@link SipDelegateConnection} instance delivered via + * {@link #onCreated(SipDelegateConnection)} has been destroyed. This interface should no longer + * be used for any SIP message handling. + * + * @param reason The reason for the failure. + */ + void onDestroyed(@SipDelegateManager.SipDelegateDestroyReason int reason); +} diff --git a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java index 12abdd1d7e11..a6f5c45445f5 100644 --- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java @@ -17,6 +17,8 @@ package android.telephony.ims.stub; import android.annotation.IntDef; +import android.annotation.IntRange; +import android.annotation.Nullable; import android.annotation.SystemApi; import android.net.Uri; import android.os.RemoteException; @@ -126,6 +128,57 @@ public class ImsRegistrationImplBase { } /** + * Called by the framework to request that the ImsService perform the network registration + * of all SIP delegates associated with this ImsService. + * <p> + * If the SIP delegate feature tag configuration has changed, then this method will be + * called in order to let the ImsService know that it can pick up these changes in the IMS + * registration. + * @hide + */ + public void updateSipDelegateRegistration() { + // Stub implementation, ImsService should implement this + } + + + /** + * Called by the framework to request that the ImsService perform the network deregistration of + * all SIP delegates associated with this ImsService. + * <p> + * This is typically called in situations where the user has changed the configuration of the + * device (for example, the default messaging application) and the framework is reconfiguring + * the tags associated with each IMS application. + * <p> + * This should not affect the registration of features managed by the ImsService itself, such as + * feature tags related to MMTEL registration. + * @hide + */ + public void triggerSipDelegateDeregistration() { + // Stub implementation, ImsService should implement this + } + + /** + * Called by the framework to notify the ImsService that a SIP delegate connection has received + * a SIP message containing a permanent failure response (such as a 403) or an indication that a + * SIP response timer has timed out in response to an outgoing SIP message. This method will be + * called when this condition occurs to trigger the ImsService to tear down the full IMS + * registration and re-register again. + * + * @param sipCode The SIP error code that represents a permanent failure that was received in + * response to a request generated by the IMS application. See RFC3261 7.2 for the general + * classes of responses available here, however the codes that generate this condition may + * be carrier specific. + * @param sipReason The reason associated with the SIP error code. {@code null} if there was no + * reason associated with the error. + * @hide + */ + public void triggerNetworkReregistration(@IntRange(from = 100, to = 699) int sipCode, + @Nullable String sipReason) { + // Stub implementation, ImsService should implement this + } + + + /** * Notify the framework that the device is connected to the IMS network. * * @param imsRadioTech the radio access technology. Valid values are defined as diff --git a/telephony/java/android/telephony/ims/stub/SipDelegate.java b/telephony/java/android/telephony/ims/stub/SipDelegate.java new file mode 100644 index 000000000000..3ec97095eb00 --- /dev/null +++ b/telephony/java/android/telephony/ims/stub/SipDelegate.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims.stub; + +import android.annotation.NonNull; +import android.telephony.ims.DelegateMessageCallback; +import android.telephony.ims.ImsService; +import android.telephony.ims.SipDelegateImsConfiguration; +import android.telephony.ims.SipDelegateManager; +import android.telephony.ims.SipMessage; + +/** + * The {@link SipDelegate} is implemented by the {@link ImsService} and allows a privileged + * IMS application to use this delegate to send SIP messages as well as acknowledge the receipt of + * incoming SIP messages delivered to the application over the existing IMS registration, allowing + * for a single IMS registration for multiple IMS applications. + * <p> + * Once the SIP delegate is created for that application, + * {@link ImsRegistrationImplBase#updateSipDelegateRegistration()} will be called, indicating that + * the application is finished setting up SipDelegates and the existing IMS registration may be + * modified to include the features managed by these SipDelegates. + * <p> + * This SipDelegate will need to notify the remote application of the registration of these features + * as well as the associated {@link SipDelegateImsConfiguration} before the application can start + * sending/receiving SIP messages via the transport. See + * {@link android.telephony.ims.DelegateStateCallback} for more information. + * @hide + */ +public interface SipDelegate { + + /** + * The framework calls this method when a remote RCS application wishes to send a new outgoing + * SIP message. + * <p> + * Once sent, this SIP delegate should notify the remote application of the success or + * failure using {@link DelegateMessageCallback#onMessageSent(String)} or + * {@link DelegateMessageCallback#onMessageSendFailure(String, int)}. + * @param message The SIP message to be sent over the operator’s network. + * @param configVersion The SipDelegateImsConfiguration version used to construct the + * SipMessage. See {@link SipDelegateImsConfiguration} for more information. If the + * version specified here does not match the most recently constructed + * {@link SipDelegateImsConfiguration}, this message should fail validation checks and + * {@link DelegateMessageCallback#onMessageSendFailure} should be called with code + * {@link SipDelegateManager#MESSAGE_FAILURE_REASON_STALE_IMS_CONFIGURATION}. + */ + void sendMessage(@NonNull SipMessage message, int configVersion); + + /** + * The framework is requesting that routing resources associated with the SIP dialog using the + * provided Call-ID to be cleaned up. + * <p> + * Typically a SIP Dialog close event will be signalled by that dialog receiving a BYE or 200 OK + * message, however, in some cases, the framework will request that the ImsService close the + * dialog due to the open dialog holding up an event such as applying a provisioning change or + * handing over to another transport type. + * @param callId The call-ID header value associated with the ongoing SIP Dialog that the + * framework is requesting be closed. + */ + void closeDialog(@NonNull String callId); + + /** + * The remote application has received the SIP message and is processing it. + * @param viaTransactionId The Transaction ID found in the via header field of the + * previously sent {@link SipMessage}. + */ + void notifyMessageReceived(@NonNull String viaTransactionId); + + /** + * The remote application has either not received the SIP message or there was an error + * processing it. + * @param viaTransactionId The Transaction ID found in the via header field of the + * previously sent {@link SipMessage}. + * @param reason The reason why the message was not correctly received. + */ + void notifyMessageReceiveError(@NonNull String viaTransactionId, + @SipDelegateManager.MessageFailureReason int reason); +} diff --git a/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java b/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java index b2b2914b3739..b48f6317e413 100644 --- a/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java +++ b/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java @@ -18,27 +18,75 @@ package android.telephony.ims.stub; import android.annotation.NonNull; import android.annotation.SystemApi; +import android.os.Binder; +import android.os.IBinder; +import android.telephony.ims.DelegateMessageCallback; +import android.telephony.ims.DelegateRequest; +import android.telephony.ims.DelegateStateCallback; +import android.telephony.ims.SipDelegateManager; +import android.telephony.ims.aidl.ISipDelegate; +import android.telephony.ims.aidl.ISipDelegateMessageCallback; +import android.telephony.ims.aidl.ISipDelegateStateCallback; import android.telephony.ims.aidl.ISipTransport; +import android.telephony.ims.aidl.SipDelegateAidlWrapper; +import android.util.Log; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; import java.util.concurrent.Executor; /** - * Manages the creation and destruction of SipDelegates in order to proxy SIP traffic to other - * IMS applications in order to support IMS single registration. + * The ImsService implements this class to manage the creation and destruction of + * {@link SipDelegate}s. + * + * {@link SipDelegate}s allow the ImsService to forward SIP traffic generated and consumed by IMS + * applications as a delegate to the associated carrier's IMS Network in order to support using a + * single IMS registration for all MMTEL and RCS signalling traffic. * @hide */ @SystemApi public class SipTransportImplBase { + private static final String LOG_TAG = "SipTransportIB"; - private final Executor mBinderExecutor; - private final ISipTransport mSipTransportImpl = new ISipTransport.Stub() { + private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() { + @Override + public void binderDied() { + mBinderExecutor.execute(() -> binderDiedInternal()); + } + }; + private final ISipTransport.Stub mSipTransportImpl = new ISipTransport.Stub() { + @Override + public void createSipDelegate(DelegateRequest request, ISipDelegateStateCallback dc, + ISipDelegateMessageCallback mc) { + final long token = Binder.clearCallingIdentity(); + try { + mBinderExecutor.execute(() -> createSipDelegateInternal(request, dc, mc)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void destroySipDelegate(ISipDelegate delegate, int reason) { + final long token = Binder.clearCallingIdentity(); + try { + mBinderExecutor.execute(() -> destroySipDelegateInternal(delegate, reason)); + } finally { + Binder.restoreCallingIdentity(token); + } + } }; + private final Executor mBinderExecutor; + private final ArrayList<SipDelegateAidlWrapper> mDelegates = new ArrayList<>(); + /** * Create an implementation of SipTransportImplBase. * - * @param executor The executor that remote calls from the framework should be called on. + * @param executor The executor that remote calls from the framework will be called on. This + * includes the methods here as well as the methods in {@link SipDelegate}. */ public SipTransportImplBase(@NonNull Executor executor) { if (executor == null) { @@ -49,6 +97,79 @@ public class SipTransportImplBase { } /** + * Called by the Telephony framework to request the creation of a new {@link SipDelegate}. + * <p> + * The implementation must call {@link DelegateStateCallback#onCreated(SipDelegate, List)} with + * the {@link SipDelegate} that is associated with the {@link DelegateRequest}. + * <p> + * This method will be called on the Executor specified in + * {@link SipTransportImplBase#SipTransportImplBase(Executor)}. + * + * @param request A SIP delegate request containing the parameters that the remote RCS + * application wishes to use. + * @param dc A callback back to the remote application to be used to communicate state callbacks + * for the SipDelegate. + * @param mc A callback back to the remote application to be used to send SIP messages to the + * remote application and acknowledge the sending of outgoing SIP messages. + * @hide + */ + public void createSipDelegate(@NonNull DelegateRequest request, + @NonNull DelegateStateCallback dc, @NonNull DelegateMessageCallback mc) { + throw new UnsupportedOperationException("destroySipDelegate not implemented!"); + } + + /** + * Destroys the SipDelegate associated with a remote IMS application. + * <p> + * After the delegate is destroyed, {@link DelegateStateCallback#onDestroyed(int)} must be + * called to notify listeners of its destruction to release associated resources. + * <p> + * This method will be called on the Executor specified in + * {@link SipTransportImplBase#SipTransportImplBase(Executor)}. + * @param delegate The delegate to be destroyed. + * @param reason The reason the remote connection to this {@link SipDelegate} is being + * destroyed. + * @hide + */ + public void destroySipDelegate(@NonNull SipDelegate delegate, + @SipDelegateManager.SipDelegateDestroyReason int reason) { + throw new UnsupportedOperationException("destroySipDelegate not implemented!"); + } + + private void createSipDelegateInternal(DelegateRequest r, ISipDelegateStateCallback cb, + ISipDelegateMessageCallback mc) { + SipDelegateAidlWrapper wrapper = new SipDelegateAidlWrapper(mBinderExecutor, cb, mc); + mDelegates.add(wrapper); + createSipDelegate(r, wrapper, wrapper); + } + + private void destroySipDelegateInternal(ISipDelegate d, int reason) { + SipDelegateAidlWrapper result = null; + for (SipDelegateAidlWrapper w : mDelegates) { + if (Objects.equals(d, w.getDelegateBinder())) { + result = w; + break; + } + } + + if (result != null) { + mDelegates.remove(result); + destroySipDelegate(result.getDelegate(), reason); + } else { + Log.w(LOG_TAG, "destroySipDelegateInternal, could not findSipDelegate corresponding to " + + d); + } + } + + private void binderDiedInternal() { + for (SipDelegateAidlWrapper w : mDelegates) { + destroySipDelegate(w.getDelegate(), + SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD); + } + mDelegates.clear(); + } + + /** * @return The IInterface used by the framework. * @hide */ diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index 36d01f459fb8..d16cb16a290c 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -31,6 +31,7 @@ import android.service.carrier.CarrierIdentifier; import android.telecom.PhoneAccount; import android.telecom.PhoneAccountHandle; import android.telephony.CallForwardingInfo; +import android.telephony.CarrierBandwidth; import android.telephony.CarrierRestrictionRules; import android.telephony.CellIdentity; import android.telephony.CellInfo; @@ -2232,4 +2233,10 @@ interface ITelephony { * @return true if dual connectivity is enabled else false */ boolean isNrDualConnectivityEnabled(int subId); + + /** + * Get carrier bandwidth per primary and secondary carrier + * @return CarrierBandwidth with bandwidth of both primary and secondary carrier. + */ + CarrierBandwidth getCarrierBandwidth(int subId); } diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java index 7abe1893dd9e..cd9406cf3481 100644 --- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java +++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java @@ -35,7 +35,6 @@ import static android.net.NetworkStats.ROAMING_YES; import static android.net.NetworkStats.SET_ALL; import static android.net.NetworkStats.SET_DEFAULT; import static android.net.NetworkStats.SET_FOREGROUND; -import static android.net.NetworkStats.STATS_PER_IFACE; import static android.net.NetworkStats.STATS_PER_UID; import static android.net.NetworkStats.TAG_ALL; import static android.net.NetworkStats.TAG_NONE; @@ -994,7 +993,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { public void testTethering() throws Exception { // pretend first mobile network comes online expectDefaultSettings(); - NetworkState[] states = new NetworkState[] {buildMobile3gState(IMSI_1)}; + final NetworkState[] states = new NetworkState[]{buildMobile3gState(IMSI_1)}; expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats()); @@ -1004,23 +1003,39 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { incrementCurrentTime(HOUR_IN_MILLIS); expectDefaultSettings(); + // Register custom provider and retrieve callback. + final TestableNetworkStatsProviderBinder provider = + new TestableNetworkStatsProviderBinder(); + final INetworkStatsProviderCallback cb = + mService.registerNetworkStatsProvider("TEST-TETHERING-OFFLOAD", provider); + assertNotNull(cb); + final long now = getElapsedRealtime(); + // Traffic seen by kernel counters (includes software tethering). - final NetworkStats ifaceStats = new NetworkStats(getElapsedRealtime(), 1) + final NetworkStats swIfaceStats = new NetworkStats(now, 1) .insertEntry(TEST_IFACE, 1536L, 12L, 384L, 3L); // Hardware tethering traffic, not seen by kernel counters. - final NetworkStats tetherStatsHardware = new NetworkStats(getElapsedRealtime(), 1) - .insertEntry(TEST_IFACE, 512L, 4L, 128L, 1L); + final NetworkStats tetherHwIfaceStats = new NetworkStats(now, 1) + .insertEntry(new NetworkStats.Entry(TEST_IFACE, UID_ALL, SET_DEFAULT, + TAG_NONE, METERED_YES, ROAMING_NO, DEFAULT_NETWORK_YES, + 512L, 4L, 128L, 1L, 0L)); + final NetworkStats tetherHwUidStats = new NetworkStats(now, 1) + .insertEntry(new NetworkStats.Entry(TEST_IFACE, UID_TETHERING, SET_DEFAULT, + TAG_NONE, METERED_YES, ROAMING_NO, DEFAULT_NETWORK_YES, + 512L, 4L, 128L, 1L, 0L)); + cb.notifyStatsUpdated(0 /* unused */, tetherHwIfaceStats, tetherHwUidStats); - // Traffic for UID_RED. - final NetworkStats uidStats = new NetworkStats(getElapsedRealtime(), 1) + // Fake some traffic done by apps on the device (as opposed to tethering), and record it + // into UID stats (as opposed to iface stats). + final NetworkStats localUidStats = new NetworkStats(now, 1) .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L); - // All tethering traffic, both hardware and software. - final NetworkStats tetherStats = new NetworkStats(getElapsedRealtime(), 1) - .insertEntry(TEST_IFACE, UID_TETHERING, SET_DEFAULT, TAG_NONE, 1920L, 14L, 384L, 2L, + // Software per-uid tethering traffic. + final NetworkStats tetherSwUidStats = new NetworkStats(now, 1) + .insertEntry(TEST_IFACE, UID_TETHERING, SET_DEFAULT, TAG_NONE, 1408L, 10L, 256L, 1L, 0L); - expectNetworkStatsSummary(ifaceStats, tetherStatsHardware); - expectNetworkStatsUidDetail(uidStats, tetherStats); + expectNetworkStatsSummary(swIfaceStats); + expectNetworkStatsUidDetail(localUidStats, tetherSwUidStats); forcePollAndWaitForIdle(); // verify service recorded history @@ -1362,12 +1377,6 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { } private void expectNetworkStatsSummary(NetworkStats summary) throws Exception { - expectNetworkStatsSummary(summary, new NetworkStats(0L, 0)); - } - - private void expectNetworkStatsSummary(NetworkStats summary, NetworkStats tetherStats) - throws Exception { - expectNetworkStatsTethering(STATS_PER_IFACE, tetherStats); expectNetworkStatsSummaryDev(summary.clone()); expectNetworkStatsSummaryXt(summary.clone()); } @@ -1380,11 +1389,6 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { when(mStatsFactory.readNetworkStatsSummaryXt()).thenReturn(summary); } - private void expectNetworkStatsTethering(int how, NetworkStats stats) - throws Exception { - when(mNetManager.getNetworkStatsTethering(how)).thenReturn(stats); - } - private void expectNetworkStatsUidDetail(NetworkStats detail) throws Exception { expectNetworkStatsUidDetail(detail, new NetworkStats(0L, 0)); } diff --git a/wifi/Android.bp b/wifi/Android.bp index 1cc5073c5f71..8b89959a4f05 100644 --- a/wifi/Android.bp +++ b/wifi/Android.bp @@ -76,25 +76,34 @@ test_access_hidden_api_whitelist = [ "//packages/apps/Settings/tests/robotests", // TODO(b/161767237): remove ] -// wifi-service needs pre-jarjared version of framework-wifi so it can reference copied utility -// classes before they are renamed. -java_library { - name: "framework-wifi-pre-jarjar", +// defaults shared between `framework-wifi` & `framework-wifi-pre-jarjar` +// java_sdk_library `framework-wifi` needs sources to generate stubs, so it cannot reuse +// `framework-wifi-pre-jarjar` +java_defaults { + name: "framework-wifi-defaults", defaults: ["wifi-module-sdk-version-defaults"], - sdk_version: "module_current", static_libs: [ "framework-wifi-util-lib", "android.hardware.wifi-V1.0-java-constants", + "modules-utils-build", ], libs: [ - "framework-annotations-lib", "unsupportedappusage", // for android.compat.annotation.UnsupportedAppUsage ], srcs: [ ":framework-wifi-updatable-sources", ":framework-wifi-util-lib-aidls", ], - // java_api_finder must accompany `srcs` +} + +// wifi-service needs pre-jarjared version of framework-wifi so it can reference copied utility +// classes before they are renamed. +java_library { + name: "framework-wifi-pre-jarjar", + defaults: ["framework-wifi-defaults"], + sdk_version: "module_current", + libs: ["framework-annotations-lib"], + // java_api_finder must accompany `srcs` (`srcs` defined in `framework-wifi-defaults`) plugins: ["java_api_finder"], installable: false, visibility: [ @@ -108,18 +117,7 @@ java_sdk_library { name: "framework-wifi", defaults: [ "framework-module-defaults", - "wifi-module-sdk-version-defaults", - ], - static_libs: [ - "framework-wifi-util-lib", - "android.hardware.wifi-V1.0-java-constants", - ], - libs: [ - "unsupportedappusage", // for android.compat.annotation.UnsupportedAppUsage - ], - srcs: [ - ":framework-wifi-updatable-sources", - ":framework-wifi-util-lib-aidls", + "framework-wifi-defaults", ], jarjar_rules: ":wifi-jarjar-rules", diff --git a/wifi/jarjar-rules.txt b/wifi/jarjar-rules.txt index b489be23b737..ff06a180b8c1 100644 --- a/wifi/jarjar-rules.txt +++ b/wifi/jarjar-rules.txt @@ -124,3 +124,4 @@ rule com.android.internal.util.Preconditions* com.android.wifi.x.@0 rule com.android.internal.util.Protocol* com.android.wifi.x.@0 rule com.android.net.module.util.** com.android.wifi.x.@0 +rule com.android.modules.utils.** com.android.wifi.x.@0 diff --git a/wifi/tests/Android.bp b/wifi/tests/Android.bp index 6a39959e8cfd..b710a1492d8c 100644 --- a/wifi/tests/Android.bp +++ b/wifi/tests/Android.bp @@ -31,10 +31,11 @@ android_test { static_libs: [ "androidx.test.rules", "core-test-rules", + "frameworks-base-testutils", "guava", "mockito-target-minus-junit4", + "modules-utils-build", "net-tests-utils", - "frameworks-base-testutils", "truth-prebuilt", ], @@ -47,4 +48,8 @@ android_test { "device-tests", "mts", ], + + // static libs used by both framework-wifi & FrameworksWifiApiTests. Need to rename test usage + // to a different package name to prevent conflict with the copy in production code. + jarjar_rules: "test-jarjar-rules.txt", } diff --git a/wifi/tests/test-jarjar-rules.txt b/wifi/tests/test-jarjar-rules.txt new file mode 100644 index 000000000000..41b97abb87b5 --- /dev/null +++ b/wifi/tests/test-jarjar-rules.txt @@ -0,0 +1 @@ +rule com.android.modules.utils.** com.android.wifi.test.x.@0 |