From c50aeb319a64c9a0cfd715fc30f02bb63d8bdb1a Mon Sep 17 00:00:00 2001 From: Brad Ebinger Date: Wed, 23 Sep 2020 16:54:20 -0700 Subject: [PATCH 1/2] Pipe through new SipTransport IInterface to listeners Create base SipTransport implementation and make it available to listeners. Expose new SipDelegate creation capability to the framework from the ImsService. The next CL will start the integration of the SipDelegateManager with the framework and SipTransport. Bug: 154763999 Test: atest FrameworksTelephonyTests TeleServiceTests Merged-In: I9a51b850f370a865c9d9109f238e8ed2eea4b6f6 Change-Id: I9a51b850f370a865c9d9109f238e8ed2eea4b6f6 --- .../android/telephony/ims/ImsService.java | 95 ++++++++++++++++++- .../ims/aidl/IImsServiceController.aidl | 3 + .../telephony/ims/aidl/ISipTransport.aidl | 24 +++++ .../ims/stub/SipTransportImplBase.java | 57 +++++++++++ .../com/android/ims/ImsFeatureContainer.java | 17 +++- 5 files changed, 192 insertions(+), 4 deletions(-) create mode 100644 telephony/java/android/telephony/ims/aidl/ISipTransport.aidl create mode 100644 telephony/java/android/telephony/ims/stub/SipTransportImplBase.java diff --git a/telephony/java/android/telephony/ims/ImsService.java b/telephony/java/android/telephony/ims/ImsService.java index 75aeb0419cff7..c806d5c8ed3a0 100644 --- a/telephony/java/android/telephony/ims/ImsService.java +++ b/telephony/java/android/telephony/ims/ImsService.java @@ -17,6 +17,7 @@ package android.telephony.ims; import android.annotation.LongDef; +import android.annotation.Nullable; import android.annotation.SystemApi; import android.app.Service; import android.content.Intent; @@ -29,12 +30,14 @@ import android.telephony.ims.aidl.IImsRcsFeature; import android.telephony.ims.aidl.IImsRegistration; import android.telephony.ims.aidl.IImsServiceController; import android.telephony.ims.aidl.IImsServiceControllerListener; +import android.telephony.ims.aidl.ISipTransport; import android.telephony.ims.feature.ImsFeature; import android.telephony.ims.feature.MmTelFeature; import android.telephony.ims.feature.RcsFeature; import android.telephony.ims.stub.ImsConfigImplBase; import android.telephony.ims.stub.ImsFeatureConfiguration; import android.telephony.ims.stub.ImsRegistrationImplBase; +import android.telephony.ims.stub.SipTransportImplBase; import android.util.Log; import android.util.SparseArray; @@ -103,18 +106,47 @@ public class ImsService extends Service { /** * This ImsService supports the capability to place emergency calls over MMTEL. + *

+ * Note: This should never be set by {@link #getImsServiceCapabilities()}, as whether it is + * there or not depends on whether or not {@link ImsFeature#FEATURE_EMERGENCY_MMTEL} is defined + * for this ImsService. If it is set, it will be removed during sanitization before the final + * capabilities bitfield is sent back to the framework. * @hide This is encoded into the {@link ImsFeature#FEATURE_EMERGENCY_MMTEL}, but we will be * adding other capabilities in a central location, so track this capability here as well. */ public static final long CAPABILITY_EMERGENCY_OVER_MMTEL = 1 << 0; + /** + * This ImsService supports the capability to create SIP delegates for other IMS applications + * to use to proxy SIP messaging traffic through it. + *

+ * In order for the framework to report SipDelegate creation as being available for this + * ImsService implementation, this ImsService must report this capability flag in + * {@link #getImsServiceCapabilities()}, {@link #getSipTransport(int)} must not return null, and + * this ImsService MUST report the ability to create both {@link ImsFeature#FEATURE_MMTEL} and + * {@link ImsFeature#FEATURE_RCS} features. + * @hide + */ + public static final long CAPABILITY_SIP_DELEGATE_CREATION = 1 << 1; + + /** + * Used for internal correctness checks of capabilities set by the ImsService implementation and + * tracks the index of the largest defined flag in the capabilities long. + * @hide + */ + public static final long CAPABILITY_MAX_INDEX = + Long.numberOfTrailingZeros(CAPABILITY_SIP_DELEGATE_CREATION); + /** * @hide */ @LongDef(flag = true, prefix = "CAPABILITY_", value = { - CAPABILITY_EMERGENCY_OVER_MMTEL + // CAPABILITY_EMERGENCY_OVER_MMTEL is not included here because it is managed by + // whether or not ImsFeature.FEATURE_EMERGENCY_MMTEL feature is set and should + // not be set by users of ImsService. + CAPABILITY_SIP_DELEGATE_CREATION }) @Retention(RetentionPolicy.SOURCE) public @interface ImsServiceCapability {} @@ -125,6 +157,7 @@ public class ImsService extends Service { */ private static final Map CAPABILITIES_LOG_MAP = new HashMap() {{ put(CAPABILITY_EMERGENCY_OVER_MMTEL, "EMERGENCY_OVER_MMTEL"); + put(CAPABILITY_SIP_DELEGATE_CREATION, "SIP_DELEGATE_CREATION"); }}; /** @@ -198,6 +231,17 @@ public class ImsService extends Service { return ImsService.this.querySupportedImsFeatures(); } + @Override + public long getImsServiceCapabilities() { + long caps = ImsService.this.getImsServiceCapabilities(); + long sanitizedCaps = sanitizeCapabilities(caps); + if (caps != sanitizedCaps) { + Log.w(LOG_TAG, "removing invalid bits from field: 0x" + + Long.toHexString(caps ^ sanitizedCaps)); + } + return sanitizedCaps; + } + @Override public void notifyImsServiceReadyForFeatureCreation() { ImsService.this.readyForFeatureCreation(); @@ -215,6 +259,12 @@ public class ImsService extends Service { return r != null ? r.getBinder() : null; } + @Override + public ISipTransport getSipTransport(int slotId) { + SipTransportImplBase s = ImsService.this.getSipTransport(slotId); + return s != null ? s.getBinder() : null; + } + @Override public void enableIms(int slotId) { ImsService.this.enableIms(slotId); @@ -368,6 +418,17 @@ public class ImsService extends Service { mListener.onUpdateSupportedImsFeatures(c); } + /** + * The optional capabilities that this ImsService supports. + *

+ * This should be a static configuration and should not change at runtime. + * @return The optional static capabilities of this ImsService implementation. + * @hide + */ + public @ImsServiceCapability long getImsServiceCapabilities() { + return 0L; + } + /** * The ImsService has been bound and is ready for ImsFeature creation based on the Features that * the ImsService has registered for with the framework, either in the manifest or via @@ -441,7 +502,37 @@ public class ImsService extends Service { } /** - * @return A string representation of the ImsService capabilties for logging. + * Return the {@link SipTransportImplBase} implementation associated with the provided slot. + *

+ * This is an optional interface used for devices that must support IMS single registration and + * proxy SIP traffic to remote IMS applications. If this is not supported for this IMS service, + * this method should return {@code null}. If this feature is supported, then this method must + * never be {@code null} and the optional ImsService capability flag + * {@link #CAPABILITY_SIP_DELEGATE_CREATION} must be set in + * {@link #getImsServiceCapabilities()}. Otherwise the framework will assume this feature is not + * supported for this ImsService. + * @param slotId The slot that is associated with the SipTransport implementation. + * @return the SipTransport implementation for the specified slot. + * @hide Keep this hidden until there is something to expose in SipTransport. + */ + public @Nullable SipTransportImplBase getSipTransport(int slotId) { + return null; + } + + private static long sanitizeCapabilities(@ImsServiceCapability long caps) { + long filter = 0xFFFFFFFFFFFFFFFFL; + // pad the "allowed" set with zeros + filter <<= CAPABILITY_MAX_INDEX + 1; + // remove values above the allowed set. + caps &= ~filter; + // CAPABILITY_EMERGENCY_OVER_MMTEL should also not be set here, will be set by telephony + // internally. + caps &= ~CAPABILITY_EMERGENCY_OVER_MMTEL; + return caps; + } + + /** + * @return A string representation of the ImsService capabilities for logging. * @hide */ public static String getCapabilitiesString(@ImsServiceCapability long caps) { diff --git a/telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl b/telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl index c956cbcc816cc..c6966b3cf53e8 100644 --- a/telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl +++ b/telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl @@ -21,6 +21,7 @@ import android.telephony.ims.aidl.IImsRcsFeature; import android.telephony.ims.aidl.IImsConfig; import android.telephony.ims.aidl.IImsRegistration; import android.telephony.ims.aidl.IImsServiceControllerListener; +import android.telephony.ims.aidl.ISipTransport; import android.telephony.ims.stub.ImsFeatureConfiguration; import com.android.ims.internal.IImsFeatureStatusCallback; @@ -34,6 +35,7 @@ interface IImsServiceController { IImsMmTelFeature createMmTelFeature(int slotId); IImsRcsFeature createRcsFeature(int slotId); ImsFeatureConfiguration querySupportedImsFeatures(); + long getImsServiceCapabilities(); void addFeatureStatusCallback(int slotId, int featureType, in IImsFeatureStatusCallback c); void removeFeatureStatusCallback(int slotId, int featureType, in IImsFeatureStatusCallback c); // Synchronous call to ensure the ImsService is ready before continuing with feature creation. @@ -41,6 +43,7 @@ interface IImsServiceController { void removeImsFeature(int slotId, int featureType); IImsConfig getConfig(int slotId); IImsRegistration getRegistration(int slotId); + ISipTransport getSipTransport(int slotId); oneway void enableIms(int slotId); oneway void disableIms(int slotId); } diff --git a/telephony/java/android/telephony/ims/aidl/ISipTransport.aidl b/telephony/java/android/telephony/ims/aidl/ISipTransport.aidl new file mode 100644 index 0000000000000..fe233430ffad1 --- /dev/null +++ b/telephony/java/android/telephony/ims/aidl/ISipTransport.aidl @@ -0,0 +1,24 @@ +/* + * 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; + +/** + * Interface for commands to the SIP Transport implementation. + * {@hide} + */ +interface ISipTransport { +} diff --git a/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java b/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java new file mode 100644 index 0000000000000..17bd4b14925f1 --- /dev/null +++ b/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java @@ -0,0 +1,57 @@ +/* + * 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.aidl.ISipTransport; + +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. + * + * @hide Until there is an implementation, keep this hidden + */ +public class SipTransportImplBase { + + private final Executor mBinderExecutor; + private final ISipTransport mSipTransportImpl = new ISipTransport.Stub() { + + }; + + /** + * Create an implementation of SipTransportImplBase. + * + * @param executor The executor that remote calls from the framework should be called on. + */ + public SipTransportImplBase(@NonNull Executor executor) { + if (executor == null) { + throw new IllegalArgumentException("executor must not be null"); + } + + mBinderExecutor = executor; + } + + /** + * @return The IInterface used by the framework. + * @hide + */ + public ISipTransport getBinder() { + return mSipTransportImpl; + } +} diff --git a/telephony/java/com/android/ims/ImsFeatureContainer.java b/telephony/java/com/android/ims/ImsFeatureContainer.java index b259679ea1bf8..80c1d43480ccc 100644 --- a/telephony/java/com/android/ims/ImsFeatureContainer.java +++ b/telephony/java/com/android/ims/ImsFeatureContainer.java @@ -17,12 +17,14 @@ package com.android.ims; import android.annotation.NonNull; +import android.annotation.Nullable; import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; import android.telephony.ims.ImsService; import android.telephony.ims.aidl.IImsConfig; import android.telephony.ims.aidl.IImsRegistration; +import android.telephony.ims.aidl.ISipTransport; import android.telephony.ims.feature.ImsFeature; import java.util.Objects; @@ -48,6 +50,11 @@ public final class ImsFeatureContainer implements Parcelable { */ public final IImsRegistration imsRegistration; + /** + * An optional interface containing the SIP transport implementation from the ImsService. + */ + public final ISipTransport sipTransport; + /** * State of the feature that is being tracked. */ @@ -66,10 +73,11 @@ public final class ImsFeatureContainer implements Parcelable { * @param initialCaps The initial capabilities that the ImsService supports. */ public ImsFeatureContainer(@NonNull IBinder iFace, @NonNull IImsConfig iConfig, - @NonNull IImsRegistration iReg, long initialCaps) { + @NonNull IImsRegistration iReg, @Nullable ISipTransport transport, long initialCaps) { imsFeature = iFace; imsConfig = iConfig; imsRegistration = iReg; + sipTransport = transport; mCapabilities = initialCaps; } @@ -80,6 +88,7 @@ public final class ImsFeatureContainer implements Parcelable { imsFeature = in.readStrongBinder(); imsConfig = IImsConfig.Stub.asInterface(in.readStrongBinder()); imsRegistration = IImsRegistration.Stub.asInterface(in.readStrongBinder()); + sipTransport = ISipTransport.Stub.asInterface(in.readStrongBinder()); mState = in.readInt(); mCapabilities = in.readLong(); } @@ -123,13 +132,15 @@ public final class ImsFeatureContainer implements Parcelable { return imsFeature.equals(that.imsFeature) && imsConfig.equals(that.imsConfig) && imsRegistration.equals(that.imsRegistration) && + sipTransport.equals(that.sipTransport) && mState == that.getState() && mCapabilities == that.getCapabilities(); } @Override public int hashCode() { - return Objects.hash(imsFeature, imsConfig, imsRegistration, mState, mCapabilities); + return Objects.hash(imsFeature, imsConfig, imsRegistration, sipTransport, mState, + mCapabilities); } @Override @@ -138,6 +149,7 @@ public final class ImsFeatureContainer implements Parcelable { "imsFeature=" + imsFeature + ", imsConfig=" + imsConfig + ", imsRegistration=" + imsRegistration + + ", sipTransport=" + sipTransport + ", state=" + ImsFeature.STATE_LOG_MAP.get(mState) + ", capabilities = " + ImsService.getCapabilitiesString(mCapabilities) + '}'; @@ -153,6 +165,7 @@ public final class ImsFeatureContainer implements Parcelable { dest.writeStrongBinder(imsFeature); dest.writeStrongInterface(imsConfig); dest.writeStrongInterface(imsRegistration); + dest.writeStrongInterface(sipTransport); dest.writeInt(mState); dest.writeLong(mCapabilities); } From 4b14954fb45bfac3bcf695ff05a7a2e584204131 Mon Sep 17 00:00:00 2001 From: Brad Ebinger Date: Wed, 23 Sep 2020 17:00:10 -0700 Subject: [PATCH 2/2] Add isSupported implementation for SipTransport Add implementation for SipDelegateManager#isSupported as well as CTS tests. Bug: 154763999 Test: atest CtsTelephonyTestCases Merged-In: I7fbd39a8ff17c4e947fdbcca148857684faa3252 Change-Id: I7fbd39a8ff17c4e947fdbcca148857684faa3252 --- api/current.txt | 1 + api/system-current.txt | 15 +++ non-updatable-api/current.txt | 1 + non-updatable-api/system-current.txt | 15 +++ telephony/api/system-current.txt | 15 +++ .../telephony/CarrierConfigManager.java | 15 +++ .../java/android/telephony/ImsManager.java | 21 +++++ .../android/telephony/ims/ImsService.java | 12 ++- .../telephony/ims/SipDelegateManager.java | 93 +++++++++++++++++++ .../telephony/ims/aidl/IImsRcsController.aidl | 3 + .../ims/stub/SipTransportImplBase.java | 5 +- 11 files changed, 191 insertions(+), 5 deletions(-) create mode 100644 telephony/java/android/telephony/ims/SipDelegateManager.java diff --git a/api/current.txt b/api/current.txt index 22b8c456f6552..90c0eab439c42 100644 --- a/api/current.txt +++ b/api/current.txt @@ -46887,6 +46887,7 @@ package android.telephony { } public static final class CarrierConfigManager.Ims { + field public static final String KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL = "ims.ims_single_registration_required_bool"; field public static final String KEY_PREFIX = "ims."; field public static final String KEY_WIFI_OFF_DEFERRING_TIME_MILLIS_INT = "ims.wifi_off_deferring_time_millis_int"; } diff --git a/api/system-current.txt b/api/system-current.txt index 6475d81067cde..11b2828e62472 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -11904,6 +11904,10 @@ package android.telephony.ims { field @NonNull public static final android.os.Parcelable.Creator CREATOR; } + public class ImsManager { + method @NonNull public android.telephony.ims.SipDelegateManager getSipDelegateManager(int); + } + public class ImsMmTelManager implements android.telephony.ims.RegistrationManager { method @Deprecated @NonNull @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public static android.telephony.ims.ImsMmTelManager createForSubscriptionId(int); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getFeatureState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer) throws android.telephony.ims.ImsException; @@ -11939,10 +11943,13 @@ package android.telephony.ims { method public void disableIms(int); method public void enableIms(int); method public android.telephony.ims.stub.ImsConfigImplBase getConfig(int); + method public long getImsServiceCapabilities(); method public android.telephony.ims.stub.ImsRegistrationImplBase getRegistration(int); + method @Nullable public android.telephony.ims.stub.SipTransportImplBase getSipTransport(int); method public final void onUpdateSupportedImsFeatures(android.telephony.ims.stub.ImsFeatureConfiguration) throws android.os.RemoteException; method public android.telephony.ims.stub.ImsFeatureConfiguration querySupportedImsFeatures(); method public void readyForFeatureCreation(); + field public static final long CAPABILITY_SIP_DELEGATE_CREATION = 2L; // 0x2L } public final class ImsSsData implements android.os.Parcelable { @@ -12188,6 +12195,10 @@ package android.telephony.ims { method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean) throws android.telephony.ims.ImsException; } + public class SipDelegateManager { + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isSupported() throws android.telephony.ims.ImsException; + } + } package android.telephony.ims.feature { @@ -12437,6 +12448,10 @@ package android.telephony.ims.stub { method public int updateColr(int); } + public class SipTransportImplBase { + ctor public SipTransportImplBase(@NonNull java.util.concurrent.Executor); + } + } package android.telephony.mbms { diff --git a/non-updatable-api/current.txt b/non-updatable-api/current.txt index da3d0f7ff0599..e7306367daf51 100644 --- a/non-updatable-api/current.txt +++ b/non-updatable-api/current.txt @@ -45055,6 +45055,7 @@ package android.telephony { } public static final class CarrierConfigManager.Ims { + field public static final String KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL = "ims.ims_single_registration_required_bool"; field public static final String KEY_PREFIX = "ims."; field public static final String KEY_WIFI_OFF_DEFERRING_TIME_MILLIS_INT = "ims.wifi_off_deferring_time_millis_int"; } diff --git a/non-updatable-api/system-current.txt b/non-updatable-api/system-current.txt index 7d5d0a65d43ee..515d368db9e27 100644 --- a/non-updatable-api/system-current.txt +++ b/non-updatable-api/system-current.txt @@ -10786,6 +10786,10 @@ package android.telephony.ims { field @NonNull public static final android.os.Parcelable.Creator CREATOR; } + public class ImsManager { + method @NonNull public android.telephony.ims.SipDelegateManager getSipDelegateManager(int); + } + public class ImsMmTelManager implements android.telephony.ims.RegistrationManager { method @Deprecated @NonNull @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public static android.telephony.ims.ImsMmTelManager createForSubscriptionId(int); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getFeatureState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer) throws android.telephony.ims.ImsException; @@ -10821,10 +10825,13 @@ package android.telephony.ims { method public void disableIms(int); method public void enableIms(int); method public android.telephony.ims.stub.ImsConfigImplBase getConfig(int); + method public long getImsServiceCapabilities(); method public android.telephony.ims.stub.ImsRegistrationImplBase getRegistration(int); + method @Nullable public android.telephony.ims.stub.SipTransportImplBase getSipTransport(int); method public final void onUpdateSupportedImsFeatures(android.telephony.ims.stub.ImsFeatureConfiguration) throws android.os.RemoteException; method public android.telephony.ims.stub.ImsFeatureConfiguration querySupportedImsFeatures(); method public void readyForFeatureCreation(); + field public static final long CAPABILITY_SIP_DELEGATE_CREATION = 2L; // 0x2L } public final class ImsSsData implements android.os.Parcelable { @@ -11070,6 +11077,10 @@ package android.telephony.ims { method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean) throws android.telephony.ims.ImsException; } + public class SipDelegateManager { + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isSupported() throws android.telephony.ims.ImsException; + } + } package android.telephony.ims.feature { @@ -11319,6 +11330,10 @@ package android.telephony.ims.stub { method public int updateColr(int); } + public class SipTransportImplBase { + ctor public SipTransportImplBase(@NonNull java.util.concurrent.Executor); + } + } package android.telephony.mbms { diff --git a/telephony/api/system-current.txt b/telephony/api/system-current.txt index 5ad377273b90f..f4b7b509be56b 100644 --- a/telephony/api/system-current.txt +++ b/telephony/api/system-current.txt @@ -1361,6 +1361,10 @@ package android.telephony.ims { field @NonNull public static final android.os.Parcelable.Creator CREATOR; } + public class ImsManager { + method @NonNull public android.telephony.ims.SipDelegateManager getSipDelegateManager(int); + } + public class ImsMmTelManager implements android.telephony.ims.RegistrationManager { method @Deprecated @NonNull @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public static android.telephony.ims.ImsMmTelManager createForSubscriptionId(int); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getFeatureState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer) throws android.telephony.ims.ImsException; @@ -1396,10 +1400,13 @@ package android.telephony.ims { method public void disableIms(int); method public void enableIms(int); method public android.telephony.ims.stub.ImsConfigImplBase getConfig(int); + method public long getImsServiceCapabilities(); method public android.telephony.ims.stub.ImsRegistrationImplBase getRegistration(int); + method @Nullable public android.telephony.ims.stub.SipTransportImplBase getSipTransport(int); method public final void onUpdateSupportedImsFeatures(android.telephony.ims.stub.ImsFeatureConfiguration) throws android.os.RemoteException; method public android.telephony.ims.stub.ImsFeatureConfiguration querySupportedImsFeatures(); method public void readyForFeatureCreation(); + field public static final long CAPABILITY_SIP_DELEGATE_CREATION = 2L; // 0x2L } public final class ImsSsData implements android.os.Parcelable { @@ -1645,6 +1652,10 @@ package android.telephony.ims { method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean) throws android.telephony.ims.ImsException; } + public class SipDelegateManager { + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isSupported() throws android.telephony.ims.ImsException; + } + } package android.telephony.ims.feature { @@ -1894,6 +1905,10 @@ package android.telephony.ims.stub { method public int updateColr(int); } + public class SipTransportImplBase { + ctor public SipTransportImplBase(@NonNull java.util.concurrent.Executor); + } + } package android.telephony.mbms { diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index a4f48af93a8d8..88a700e49a98a 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -3785,11 +3785,26 @@ public class CarrierConfigManager { public static final String KEY_WIFI_OFF_DEFERRING_TIME_MILLIS_INT = KEY_PREFIX + "wifi_off_deferring_time_millis_int"; + /** + * A boolean flag specifying whether or not this carrier requires one IMS registration for + * all IMS services (MMTEL and RCS). + *

+ * If set to {@code true}, the IMS Service must use one IMS registration for all IMS + * services. If set to {@code false}, IMS services may use separate IMS registrations for + * MMTEL and RCS. + *

+ * The default value for this configuration is {@code false}. + * @see android.telephony.ims.SipDelegateManager + */ + public static final String KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL = + KEY_PREFIX + "ims_single_registration_required_bool"; + private Ims() {} private static PersistableBundle getDefaults() { PersistableBundle defaults = new PersistableBundle(); defaults.putInt(KEY_WIFI_OFF_DEFERRING_TIME_MILLIS_INT, 4000); + defaults.putBoolean(KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL, false); return defaults; } } diff --git a/telephony/java/android/telephony/ImsManager.java b/telephony/java/android/telephony/ImsManager.java index 3984bd769edda..28feab27a794b 100644 --- a/telephony/java/android/telephony/ImsManager.java +++ b/telephony/java/android/telephony/ImsManager.java @@ -19,6 +19,7 @@ package android.telephony.ims; import android.annotation.NonNull; import android.annotation.SdkConstant; import android.annotation.SuppressLint; +import android.annotation.SystemApi; import android.annotation.SystemService; import android.content.Context; import android.telephony.SubscriptionManager; @@ -125,4 +126,24 @@ public class ImsManager { return new ImsMmTelManager(subscriptionId); } + + /** + * Create an instance of SipDelegateManager for the subscription id specified. + *

+ * 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. + * @throws IllegalArgumentException if the subscription is invalid. + * @return a SipDelegateManager instance for the specified subscription ID. + * @hide + */ + @SystemApi + @NonNull + public SipDelegateManager getSipDelegateManager(int subscriptionId) { + if (!SubscriptionManager.isValidSubscriptionId(subscriptionId)) { + throw new IllegalArgumentException("Invalid subscription ID: " + subscriptionId); + } + + return new SipDelegateManager(mContext, subscriptionId); + } } diff --git a/telephony/java/android/telephony/ims/ImsService.java b/telephony/java/android/telephony/ims/ImsService.java index c806d5c8ed3a0..9ab5aeb9c34c8 100644 --- a/telephony/java/android/telephony/ims/ImsService.java +++ b/telephony/java/android/telephony/ims/ImsService.java @@ -18,6 +18,7 @@ package android.telephony.ims; import android.annotation.LongDef; import android.annotation.Nullable; +import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.app.Service; import android.content.Intent; @@ -125,7 +126,6 @@ public class ImsService extends Service { * {@link #getImsServiceCapabilities()}, {@link #getSipTransport(int)} must not return null, and * this ImsService MUST report the ability to create both {@link ImsFeature#FEATURE_MMTEL} and * {@link ImsFeature#FEATURE_RCS} features. - * @hide */ public static final long CAPABILITY_SIP_DELEGATE_CREATION = 1 << 1; @@ -423,9 +423,12 @@ public class ImsService extends Service { *

* This should be a static configuration and should not change at runtime. * @return The optional static capabilities of this ImsService implementation. - * @hide */ + // ImsService follows a different convention, since it is a stub class. The on* methods are + // final and call back into the framework with a state update. + @SuppressLint("OnNameExpected") public @ImsServiceCapability long getImsServiceCapabilities() { + // Stub implementation to be implemented by ImsService. return 0L; } @@ -513,9 +516,12 @@ public class ImsService extends Service { * supported for this ImsService. * @param slotId The slot that is associated with the SipTransport implementation. * @return the SipTransport implementation for the specified slot. - * @hide Keep this hidden until there is something to expose in SipTransport. */ + // ImsService follows a different convention, since it is a stub class. The on* methods are + // final and call back into the framework with a state update. + @SuppressLint("OnNameExpected") public @Nullable SipTransportImplBase getSipTransport(int slotId) { + // Stub implementation for ImsServices that do not support SipTransport. return null; } diff --git a/telephony/java/android/telephony/ims/SipDelegateManager.java b/telephony/java/android/telephony/ims/SipDelegateManager.java new file mode 100644 index 0000000000000..82c8a9cd58f4b --- /dev/null +++ b/telephony/java/android/telephony/ims/SipDelegateManager.java @@ -0,0 +1,93 @@ +/* + * 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.Manifest; +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.CarrierConfigManager; +import android.telephony.TelephonyFrameworkInitializer; +import android.telephony.ims.aidl.IImsRcsController; + +import com.android.internal.annotations.VisibleForTesting; + +/** + * 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. + * @hide + */ +@SystemApi +public class SipDelegateManager { + + private final Context mContext; + private final int mSubId; + + /** + * Only visible for testing. To instantiate an instance of this class, please use + * {@link ImsManager#getSipDelegateManager(int)}. + * @hide + */ + @VisibleForTesting + public SipDelegateManager(Context context, int subId) { + mContext = context; + mSubId = subId; + } + + /** + * Determines if creating SIP delegates are supported for the subscription specified. + *

+ * If SIP delegates are not supported on this device or the carrier associated with this + * subscription, creating a SIP delegate will always fail, as this feature is not supported. + * @return true if this device supports creating a SIP delegate and the carrier associated with + * this subscription supports single registration, false if creating SIP delegates is not + * supported. + * @throws ImsException If the remote ImsService is not available for any reason or the + * subscription associated with this instance is no longer active. See + * {@link ImsException#getCode()} for more information. + * + * @see CarrierConfigManager.Ims#KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL + */ + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public boolean isSupported() throws ImsException { + try { + IImsRcsController controller = getIImsRcsController(); + if (controller == null) { + throw new ImsException("Telephony server is down", + ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + return controller.isSipDelegateSupported(mSubId); + } catch (ServiceSpecificException e) { + throw new ImsException(e.getMessage(), e.errorCode); + } catch (RemoteException e) { + throw new ImsException(e.getMessage(), + ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + } + + private IImsRcsController getIImsRcsController() { + IBinder binder = TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getTelephonyImsServiceRegisterer() + .get(); + return IImsRcsController.Stub.asInterface(binder); + } +} diff --git a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl index e01ea9179452b..6d25a09e079f4 100644 --- a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl +++ b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl @@ -53,6 +53,9 @@ interface IImsRcsController { void registerUcePublishStateCallback(int subId, IRcsUcePublishStateCallback c); void unregisterUcePublishStateCallback(int subId, IRcsUcePublishStateCallback c); + // SipDelegateManager + boolean isSipDelegateSupported(int subId); + // Internal commands that should not be made public void registerRcsFeatureCallback(int slotId, in IImsServiceFeatureCallback callback); void unregisterImsFeatureCallback(in IImsServiceFeatureCallback callback); diff --git a/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java b/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java index 17bd4b14925f1..b2b2914b3739a 100644 --- a/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java +++ b/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java @@ -17,6 +17,7 @@ package android.telephony.ims.stub; import android.annotation.NonNull; +import android.annotation.SystemApi; import android.telephony.ims.aidl.ISipTransport; import java.util.concurrent.Executor; @@ -24,9 +25,9 @@ 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. - * - * @hide Until there is an implementation, keep this hidden + * @hide */ +@SystemApi public class SipTransportImplBase { private final Executor mBinderExecutor;