diff options
| -rw-r--r-- | core/api/current.txt | 17 | ||||
| -rw-r--r-- | core/api/system-current.txt | 2 | ||||
| -rw-r--r-- | telephony/java/android/telephony/ImsManager.java | 6 | ||||
| -rw-r--r-- | telephony/java/android/telephony/ims/ImsMmTelManager.java | 81 | ||||
| -rw-r--r-- | telephony/java/android/telephony/ims/ImsRcsManager.java | 68 | ||||
| -rw-r--r-- | telephony/java/android/telephony/ims/ImsStateCallback.java | 184 | ||||
| -rw-r--r-- | telephony/java/android/telephony/ims/SipDelegateManager.java | 69 | ||||
| -rw-r--r-- | telephony/java/com/android/internal/telephony/IImsStateCallback.aidl | 22 | ||||
| -rw-r--r-- | telephony/java/com/android/internal/telephony/ITelephony.aidl | 12 |
9 files changed, 455 insertions, 6 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index f21518c7f83d..f15d457f3c24 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -43474,8 +43474,10 @@ package android.telephony.ims { method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isVoWiFiSettingEnabled(); method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isVtSettingEnabled(); method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RegistrationManager.RegistrationCallback) throws android.telephony.ims.ImsException; + method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, "android.permission.READ_PRIVILEGED_PHONE_STATE"}) public void registerImsStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsStateCallback) throws android.telephony.ims.ImsException; method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public void registerMmTelCapabilityCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsMmTelManager.CapabilityCallback) throws android.telephony.ims.ImsException; method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback); + method public void unregisterImsStateCallback(@NonNull android.telephony.ims.ImsStateCallback); method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public void unregisterMmTelCapabilityCallback(@NonNull android.telephony.ims.ImsMmTelManager.CapabilityCallback); field public static final int WIFI_MODE_CELLULAR_PREFERRED = 1; // 0x1 field public static final int WIFI_MODE_WIFI_ONLY = 0; // 0x0 @@ -43492,7 +43494,9 @@ package android.telephony.ims { method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void getRegistrationTransportType(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); method @NonNull public android.telephony.ims.RcsUceAdapter getUceAdapter(); method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RegistrationManager.RegistrationCallback) throws android.telephony.ims.ImsException; + method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, "android.permission.READ_PRIVILEGED_PHONE_STATE", "android.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE"}) public void registerImsStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsStateCallback) throws android.telephony.ims.ImsException; method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback); + method public void unregisterImsStateCallback(@NonNull android.telephony.ims.ImsStateCallback); field public static final String ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN = "android.telephony.ims.action.SHOW_CAPABILITY_DISCOVERY_OPT_IN"; } @@ -43691,6 +43695,19 @@ package android.telephony.ims { field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsRegistrationAttributes> CREATOR; } + public abstract class ImsStateCallback { + ctor public ImsStateCallback(); + method public abstract void onAvailable(); + method public abstract void onError(); + method public abstract void onUnavailable(int); + field public static final int REASON_IMS_SERVICE_DISCONNECTED = 3; // 0x3 + field public static final int REASON_IMS_SERVICE_NOT_READY = 6; // 0x6 + field public static final int REASON_NO_IMS_SERVICE_CONFIGURED = 4; // 0x4 + field public static final int REASON_SUBSCRIPTION_INACTIVE = 5; // 0x5 + field public static final int REASON_UNKNOWN_PERMANENT_ERROR = 2; // 0x2 + field public static final int REASON_UNKNOWN_TEMPORARY_ERROR = 1; // 0x1 + } + public class RcsUceAdapter { method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isUceSettingEnabled() throws android.telephony.ims.ImsException; } diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 78e6a07c8705..84a4a44c5cf0 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -13741,7 +13741,9 @@ package android.telephony.ims { method @RequiresPermission(android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) public void createSipDelegate(@NonNull android.telephony.ims.DelegateRequest, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.stub.DelegateConnectionStateCallback, @NonNull android.telephony.ims.stub.DelegateConnectionMessageCallback) throws android.telephony.ims.ImsException; method @RequiresPermission(android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) public void destroySipDelegate(@NonNull android.telephony.ims.SipDelegateConnection, int); method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION}) public boolean isSupported() throws android.telephony.ims.ImsException; + method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION}) public void registerImsStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsStateCallback) throws android.telephony.ims.ImsException; method @RequiresPermission(android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) public void triggerFullNetworkRegistration(@NonNull android.telephony.ims.SipDelegateConnection, @IntRange(from=100, to=699) int, @Nullable String); + method public void unregisterImsStateCallback(@NonNull android.telephony.ims.ImsStateCallback); field public static final int DENIED_REASON_INVALID = 4; // 0x4 field public static final int DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE = 1; // 0x1 field public static final int DENIED_REASON_NOT_ALLOWED = 2; // 0x2 diff --git a/telephony/java/android/telephony/ImsManager.java b/telephony/java/android/telephony/ImsManager.java index 42d7707c97d9..fc76f99cf074 100644 --- a/telephony/java/android/telephony/ImsManager.java +++ b/telephony/java/android/telephony/ImsManager.java @@ -119,7 +119,7 @@ public class ImsManager { throw new IllegalArgumentException("Invalid subscription ID: " + subscriptionId); } - return new ImsRcsManager(mContext, subscriptionId, sRcsCache); + return new ImsRcsManager(mContext, subscriptionId, sRcsCache, sTelephonyCache); } /** @@ -135,7 +135,7 @@ public class ImsManager { throw new IllegalArgumentException("Invalid subscription ID: " + subscriptionId); } - return new ImsMmTelManager(subscriptionId, sTelephonyCache); + return new ImsMmTelManager(mContext, subscriptionId, sTelephonyCache); } /** @@ -157,7 +157,7 @@ public class ImsManager { throw new IllegalArgumentException("Invalid subscription ID: " + subscriptionId); } - return new SipDelegateManager(mContext, subscriptionId, sRcsCache); + return new SipDelegateManager(mContext, subscriptionId, sRcsCache, sTelephonyCache); } private static IImsRcsController getIImsRcsControllerInterface() { diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java index 36082dc8180e..683bb92845ba 100644 --- a/telephony/java/android/telephony/ims/ImsMmTelManager.java +++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java @@ -25,6 +25,7 @@ import android.annotation.RequiresPermission; import android.annotation.SuppressAutoDoc; import android.annotation.SuppressLint; import android.annotation.SystemApi; +import android.content.Context; import android.os.Binder; import android.os.RemoteException; import android.os.ServiceSpecificException; @@ -45,6 +46,7 @@ import com.android.internal.telephony.ITelephony; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Objects; import java.util.concurrent.Executor; import java.util.function.Consumer; @@ -214,6 +216,7 @@ public class ImsMmTelManager implements RegistrationManager { } } + private final Context mContext; private final int mSubId; private final BinderCacheManager<ITelephony> mBinderCache; @@ -255,6 +258,16 @@ public class ImsMmTelManager implements RegistrationManager { */ @VisibleForTesting public ImsMmTelManager(int subId, BinderCacheManager<ITelephony> binderCache) { + this(null, subId, binderCache); + } + + /** + * Only visible for testing, use {@link ImsManager#getImsMmTelManager(int)} instead. + * @hide + */ + @VisibleForTesting + public ImsMmTelManager(Context context, int subId, BinderCacheManager<ITelephony> binderCache) { + mContext = context; mSubId = subId; mBinderCache = binderCache; } @@ -1482,6 +1495,74 @@ public class ImsMmTelManager implements RegistrationManager { } } + /** + * Register a new callback, which is used to notify the registrant of changes to + * the state of the underlying IMS service that is attached to telephony to + * implement IMS functionality. If the manager is created for + * the {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID}, + * this throws an {@link ImsException}. + * + * <p>Requires Permission: + * {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE READ_PRECISE_PHONE_STATE} + * or that the calling app has carrier privileges + * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}). + * + * @param executor the Executor that will be used to call the {@link ImsStateCallback}. + * @param callback The callback instance being registered. + * @throws ImsException in the case that the callback can not be registered. + * See {@link ImsException#getCode} for more information on when this is called. + */ + @RequiresPermission(anyOf = {Manifest.permission.READ_PRECISE_PHONE_STATE, + Manifest.permission.READ_PRIVILEGED_PHONE_STATE}) + public void registerImsStateCallback(@NonNull Executor executor, + @NonNull ImsStateCallback callback) throws ImsException { + Objects.requireNonNull(callback, "Must include a non-null ImsStateCallback."); + Objects.requireNonNull(executor, "Must include a non-null Executor."); + + callback.init(executor); + ITelephony telephony = mBinderCache.listenOnBinder(callback, callback::binderDied); + if (telephony == null) { + throw new ImsException("Telephony server is down", + ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + + try { + telephony.registerImsStateCallback( + mSubId, ImsFeature.FEATURE_MMTEL, + callback.getCallbackBinder(), getOpPackageName()); + } catch (ServiceSpecificException e) { + throw new ImsException(e.getMessage(), e.errorCode); + } catch (RemoteException | IllegalStateException e) { + throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + } + + /** + * Unregisters a previously registered callback. + * + * @param callback The callback instance to be unregistered. + */ + public void unregisterImsStateCallback(@NonNull ImsStateCallback callback) { + Objects.requireNonNull(callback, "Must include a non-null ImsStateCallback."); + + ITelephony telephony = mBinderCache.removeRunnable(callback); + try { + if (telephony != null) { + telephony.unregisterImsStateCallback(callback.getCallbackBinder()); + } + } catch (RemoteException ignore) { + // ignore it + } + } + + private String getOpPackageName() { + if (mContext != null) { + return mContext.getOpPackageName(); + } else { + return null; + } + } + private ITelephony getITelephony() { return mBinderCache.getBinder(); } diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java index 8d6fa4141b5c..1b047c77d80b 100644 --- a/telephony/java/android/telephony/ims/ImsRcsManager.java +++ b/telephony/java/android/telephony/ims/ImsRcsManager.java @@ -39,9 +39,11 @@ import android.telephony.ims.stub.ImsRegistrationImplBase; import android.util.Log; import com.android.internal.telephony.IIntegerConsumer; +import com.android.internal.telephony.ITelephony; import java.util.HashMap; import java.util.Map; +import java.util.Objects; import java.util.concurrent.Executor; import java.util.function.Consumer; @@ -159,6 +161,7 @@ public class ImsRcsManager { private final int mSubId; private final Context mContext; private final BinderCacheManager<IImsRcsController> mBinderCache; + private final BinderCacheManager<ITelephony> mTelephonyBinderCache; private final Map<OnAvailabilityChangedListener, AvailabilityCallbackAdapter> mAvailabilityChangedCallbacks; @@ -167,11 +170,13 @@ public class ImsRcsManager { * @hide */ public ImsRcsManager(Context context, int subId, - BinderCacheManager<IImsRcsController> binderCache) { + BinderCacheManager<IImsRcsController> binderCache, + BinderCacheManager<ITelephony> telephonyBinderCache) { mSubId = subId; mContext = context; mBinderCache = binderCache; mAvailabilityChangedCallbacks = new HashMap<>(); + mTelephonyBinderCache = telephonyBinderCache; } /** @@ -534,6 +539,67 @@ public class ImsRcsManager { } /** + * Register a new callback, which is used to notify the registrant of changes to + * the state of the underlying IMS service that is attached to telephony to + * implement IMS functionality. If the manager is created for + * the {@link android.telephony.SubscriptionManager#DEFAULT_SUBSCRIPTION_ID}, + * this throws an {@link ImsException}. + * + * <p>Requires Permission: + * {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE READ_PRECISE_PHONE_STATE} + * or that the calling app has carrier privileges + * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}). + * + * @param executor the Executor that will be used to call the {@link ImsStateCallback}. + * @param callback The callback instance being registered. + * @throws ImsException in the case that the callback can not be registered. + * See {@link ImsException#getCode} for more information on when this is called. + */ + @RequiresPermission(anyOf = {Manifest.permission.READ_PRECISE_PHONE_STATE, + Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + Manifest.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE}) + public void registerImsStateCallback(@NonNull Executor executor, + @NonNull ImsStateCallback callback) throws ImsException { + Objects.requireNonNull(callback, "Must include a non-null ImsStateCallback."); + Objects.requireNonNull(executor, "Must include a non-null Executor."); + + callback.init(executor); + ITelephony telephony = mTelephonyBinderCache.listenOnBinder(callback, callback::binderDied); + if (telephony == null) { + throw new ImsException("Telephony server is down", + ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + + try { + telephony.registerImsStateCallback( + mSubId, ImsFeature.FEATURE_RCS, + callback.getCallbackBinder(), mContext.getOpPackageName()); + } catch (ServiceSpecificException e) { + throw new ImsException(e.getMessage(), e.errorCode); + } catch (RemoteException | IllegalStateException e) { + throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + } + + /** + * Unregisters a previously registered callback. + * + * @param callback The callback instance to be unregistered. + */ + public void unregisterImsStateCallback(@NonNull ImsStateCallback callback) { + Objects.requireNonNull(callback, "Must include a non-null ImsStateCallback."); + + ITelephony telephony = mTelephonyBinderCache.removeRunnable(callback); + try { + if (telephony != null) { + telephony.unregisterImsStateCallback(callback.getCallbackBinder()); + } + } catch (RemoteException ignore) { + // ignore it + } + } + + /** * Add the {@link OnAvailabilityChangedListener} to collection for tracking. * @param executor The executor that will be used when the publish state is changed and the * {@link OnAvailabilityChangedListener} is called. diff --git a/telephony/java/android/telephony/ims/ImsStateCallback.java b/telephony/java/android/telephony/ims/ImsStateCallback.java new file mode 100644 index 000000000000..b9ba93ff0db6 --- /dev/null +++ b/telephony/java/android/telephony/ims/ImsStateCallback.java @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2021 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.CallbackExecutor; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.os.Binder; + +import com.android.internal.telephony.IImsStateCallback; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.ref.WeakReference; +import java.util.concurrent.Executor; + +/** + * A callback class used for monitoring changes in IMS service connection states + * for a specific subscription. + * <p> + * @see ImsMmTelManager#registerImsStateCallback(Executor, ImsStateCallback) + * @see ImsRcsManager#registerImsStateCallback(Executor, ImsStateCallback) + */ +public abstract class ImsStateCallback { + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "REASON_", value = { + REASON_UNKNOWN_TEMPORARY_ERROR, + REASON_UNKNOWN_PERMANENT_ERROR, + REASON_IMS_SERVICE_DISCONNECTED, + REASON_NO_IMS_SERVICE_CONFIGURED, + REASON_SUBSCRIPTION_INACTIVE, + REASON_IMS_SERVICE_NOT_READY + }) + public @interface DisconnectedReason {} + + /** + * The underlying IMS service is temporarily unavailable for the + * associated subscription. + * {@link #onAvailable} will be called when the IMS service becomes + * available again. + */ + public static final int REASON_UNKNOWN_TEMPORARY_ERROR = 1; + + /** + * The underlying IMS service is permanently unavailable for the + * associated subscription and there will be no Manager available for + * this subscription. + */ + public static final int REASON_UNKNOWN_PERMANENT_ERROR = 2; + + /** + * The underlying IMS service has died, is reconfiguring, or has never + * come up yet and as a result is currently unavailable. + * {@link #onAvailable} will be called when the IMS service becomes + * available. All callbacks should be unregistered now and registered again + * if the IMS service moves back to available. + */ + public static final int REASON_IMS_SERVICE_DISCONNECTED = 3; + + /** + * There is no IMS service configured for the subscription ID specified. + * This is a permanent error and there will be no Manager available for + * this subscription. + */ + public static final int REASON_NO_IMS_SERVICE_CONFIGURED = 4; + + /** + * The subscription associated with this Manager has moved to an inactive + * state (e.g. SIM removed) and the IMS service has torn down the resources + * related to this subscription. This has caused this callback + * to be deregistered. The callback must be re-registered when this subscription + * becomes active in order to continue listening to the IMS service state. + */ + public static final int REASON_SUBSCRIPTION_INACTIVE = 5; + + /** + * The IMS service is connected, but in a NOT_READY state. Once the + * service moves to ready, {@link #onAvailable} will be called. + */ + public static final int REASON_IMS_SERVICE_NOT_READY = 6; + + private IImsStateCallbackStub mCallback; + + /** + * @hide + */ + public void init(@NonNull @CallbackExecutor Executor executor) { + if (executor == null) { + throw new IllegalArgumentException("ImsStateCallback Executor must be non-null"); + } + mCallback = new IImsStateCallbackStub(this, executor); + } + + /** + * Using a static class and weak reference here to avoid memory leak caused by the + * IImsStateCallback.Stub callback retaining references to the outside ImsStateCallback. + */ + private static class IImsStateCallbackStub extends IImsStateCallback.Stub { + private WeakReference<ImsStateCallback> mImsStateCallbackWeakRef; + private Executor mExecutor; + + IImsStateCallbackStub(ImsStateCallback imsStateCallback, Executor executor) { + mImsStateCallbackWeakRef = new WeakReference<ImsStateCallback>(imsStateCallback); + mExecutor = executor; + } + + Executor getExecutor() { + return mExecutor; + } + + public void onAvailable() { + ImsStateCallback callback = mImsStateCallbackWeakRef.get(); + if (callback == null) return; + + Binder.withCleanCallingIdentity( + () -> mExecutor.execute(() -> callback.onAvailable())); + } + + public void onUnavailable(int reason) { + ImsStateCallback callback = mImsStateCallbackWeakRef.get(); + if (callback == null) return; + + Binder.withCleanCallingIdentity( + () -> mExecutor.execute(() -> callback.onUnavailable(reason))); + } + } + + /** + * The IMS service has disconnected or is reporting NOT_READY and is no longer + * available to users. The user should clean up all related state and + * unregister callbacks. If it is a temporary error, {@link #onAvailable} will + * be called when the IMS service becomes available again. + * + * @param reason the specified reason + */ + public abstract void onUnavailable(@DisconnectedReason int reason); + + /** + * The IMS service is connected and is ready for communication over the + * provided Manager. + */ + public abstract void onAvailable(); + + /** + * An unexpected error has occurred and the Telephony process has crashed. This + * has caused this callback to be deregistered. The callback must be + * re-registered in order to continue listening to the IMS service state. + */ + public abstract void onError(); + + /** + * The callback to notify the death of telephony process + * @hide + */ + public final void binderDied() { + if (mCallback != null) { + mCallback.getExecutor().execute(() -> onError()); + } + } + + /** + * Return the callback binder + * @hide + */ + public IImsStateCallbackStub getCallbackBinder() { + return mCallback; + } +} diff --git a/telephony/java/android/telephony/ims/SipDelegateManager.java b/telephony/java/android/telephony/ims/SipDelegateManager.java index 5a8066320e99..f913df588f40 100644 --- a/telephony/java/android/telephony/ims/SipDelegateManager.java +++ b/telephony/java/android/telephony/ims/SipDelegateManager.java @@ -28,15 +28,16 @@ import android.content.pm.PackageManager; import android.os.RemoteException; import android.os.ServiceSpecificException; import android.telephony.BinderCacheManager; -import android.telephony.CarrierConfigManager; import android.telephony.ims.aidl.IImsRcsController; import android.telephony.ims.aidl.SipDelegateConnectionAidlWrapper; +import android.telephony.ims.feature.ImsFeature; import android.telephony.ims.stub.DelegateConnectionMessageCallback; import android.telephony.ims.stub.DelegateConnectionStateCallback; import android.telephony.ims.stub.SipDelegate; import android.util.ArrayMap; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.telephony.ITelephony; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -282,6 +283,7 @@ public class SipDelegateManager { private final Context mContext; private final int mSubId; private final BinderCacheManager<IImsRcsController> mBinderCache; + private final BinderCacheManager<ITelephony> mTelephonyBinderCache; /** * Only visible for testing. To instantiate an instance of this class, please use @@ -290,10 +292,12 @@ public class SipDelegateManager { */ @VisibleForTesting public SipDelegateManager(Context context, int subId, - BinderCacheManager<IImsRcsController> binderCache) { + BinderCacheManager<IImsRcsController> binderCache, + BinderCacheManager<ITelephony> telephonyBinderCache) { mContext = context; mSubId = subId; mBinderCache = binderCache; + mTelephonyBinderCache = telephonyBinderCache; } /** @@ -446,4 +450,65 @@ public class SipDelegateManager { + " into this method"); } } + + /** + * Register a new callback, which is used to notify the registrant of changes to + * the state of the underlying IMS service that is attached to telephony to + * implement IMS functionality. If the manager is created for + * the {@link android.telephony.SubscriptionManager#DEFAULT_SUBSCRIPTION_ID}, + * this throws an {@link ImsException}. + * + * <p>Requires Permission: + * {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE READ_PRECISE_PHONE_STATE} + * or that the calling app has carrier privileges + * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}). + * + * @param executor the Executor that will be used to call the {@link ImsStateCallback}. + * @param callback The callback instance being registered. + * @throws ImsException in the case that the callback can not be registered. + * See {@link ImsException#getCode} for more information on when this is called. + */ + @RequiresPermission(anyOf = {Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION}) + public void registerImsStateCallback(@NonNull Executor executor, + @NonNull ImsStateCallback callback) throws ImsException { + Objects.requireNonNull(callback, "Must include a non-null ImsStateCallback."); + Objects.requireNonNull(executor, "Must include a non-null Executor."); + + callback.init(executor); + ITelephony telephony = mTelephonyBinderCache.listenOnBinder(callback, callback::binderDied); + if (telephony == null) { + throw new ImsException("Telephony server is down", + ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + + try { + telephony.registerImsStateCallback( + mSubId, ImsFeature.FEATURE_RCS, + callback.getCallbackBinder(), mContext.getOpPackageName()); + } catch (ServiceSpecificException e) { + throw new ImsException(e.getMessage(), e.errorCode); + } catch (RemoteException | IllegalStateException e) { + throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + } + + /** + * Unregisters a previously registered callback. + * + * @param callback The callback instance to be unregistered. + */ + public void unregisterImsStateCallback(@NonNull ImsStateCallback callback) { + Objects.requireNonNull(callback, "Must include a non-null ImsStateCallback."); + + ITelephony telephony = mTelephonyBinderCache.removeRunnable(callback); + + try { + if (telephony != null) { + telephony.unregisterImsStateCallback(callback.getCallbackBinder()); + } + } catch (RemoteException ignore) { + // ignore it + } + } } diff --git a/telephony/java/com/android/internal/telephony/IImsStateCallback.aidl b/telephony/java/com/android/internal/telephony/IImsStateCallback.aidl new file mode 100644 index 000000000000..e04b01d26e47 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/IImsStateCallback.aidl @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2021 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 com.android.internal.telephony; + +oneway interface IImsStateCallback { + void onUnavailable(int reason); + void onAvailable(); +} diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index d586a4a38f73..6b33a6894365 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -67,6 +67,7 @@ import com.android.ims.internal.IImsServiceFeatureCallback; import com.android.internal.telephony.CellNetworkScanResult; import com.android.internal.telephony.IBooleanConsumer; import com.android.internal.telephony.ICallForwardingInfoCallback; +import com.android.internal.telephony.IImsStateCallback; import com.android.internal.telephony.IIntegerConsumer; import com.android.internal.telephony.INumberVerificationCallback; import com.android.internal.telephony.OperatorInfo; @@ -2497,4 +2498,15 @@ interface ITelephony { * NSSAIs (configured, allowed and rejected). */ void getSlicingConfig(in ResultReceiver callback); + + /** + * Register an IMS connection state callback + */ + void registerImsStateCallback(int subId, int feature, in IImsStateCallback cb, + in String callingPackage); + + /** + * Unregister an IMS connection state callback + */ + void unregisterImsStateCallback(in IImsStateCallback cb); } |