From bf9c71073384e5266a4b8aae47a2220502758dbc Mon Sep 17 00:00:00 2001 From: Brad Ebinger Date: Wed, 14 Oct 2020 10:47:37 -0700 Subject: [PATCH] Add AIDL connections for SipDelegateManager Implement AIDL backing for SipDelegateConnection, DelegateConnectionStateCallback, and DelegateConnectionMessageCallback as well as all Parcelables related to those callbacks. Implement SipDelegateConnection with an AIDL wrapper implementation, which trampolines callback events from the remote process to the local callback interface as well as wrap the SipDelegateConnection AIDL so the local process can send messages to the remote SipDelegate. The next CL will be the ImsService AIDL implementation for SipTransport/SipDelegate APIs. Test: atest FrameworksTelephonyTests Change-Id: Ic48cd0f80058d59362c63cf6381304437a81afe0 --- .../java/android/telephony/ImsManager.java | 37 ++- .../ims/DelegateRegistrationState.aidl | 19 ++ .../telephony/ims/DelegateRequest.aidl | 19 ++ .../telephony/ims/FeatureTagState.aidl | 19 ++ .../telephony/ims/ImsMmTelManager.java | 14 +- .../android/telephony/ims/ImsRcsManager.java | 6 +- .../ims/SipDelegateImsConfiguration.aidl | 19 ++ .../telephony/ims/SipDelegateManager.java | 69 +++-- .../android/telephony/ims/SipMessage.aidl | 19 ++ .../telephony/ims/aidl/IImsRcsController.aidl | 11 +- .../telephony/ims/aidl/ISipDelegate.aidl | 32 +++ .../ISipDelegateConnectionStateCallback.aidl | 34 +++ .../ims/aidl/ISipDelegateMessageCallback.aidl | 30 ++ .../SipDelegateConnectionAidlWrapper.java | 260 ++++++++++++++++++ 14 files changed, 559 insertions(+), 29 deletions(-) create mode 100644 telephony/java/android/telephony/ims/DelegateRegistrationState.aidl create mode 100644 telephony/java/android/telephony/ims/DelegateRequest.aidl create mode 100644 telephony/java/android/telephony/ims/FeatureTagState.aidl create mode 100644 telephony/java/android/telephony/ims/SipDelegateImsConfiguration.aidl create mode 100644 telephony/java/android/telephony/ims/SipMessage.aidl create mode 100644 telephony/java/android/telephony/ims/aidl/ISipDelegate.aidl create mode 100644 telephony/java/android/telephony/ims/aidl/ISipDelegateConnectionStateCallback.aidl create mode 100644 telephony/java/android/telephony/ims/aidl/ISipDelegateMessageCallback.aidl create mode 100644 telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java diff --git a/telephony/java/android/telephony/ImsManager.java b/telephony/java/android/telephony/ImsManager.java index 13f407b14f010..42d7707c97d93 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; - /** *

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 sTelephonyCache = + new BinderCacheManager<>(ImsManager::getITelephonyInterface); + private static final BinderCacheManager 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,7 +135,7 @@ public class ImsManager { throw new IllegalArgumentException("Invalid subscription ID: " + subscriptionId); } - return new ImsMmTelManager(subscriptionId); + return new ImsMmTelManager(subscriptionId, sTelephonyCache); } /** @@ -146,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/ims/DelegateRegistrationState.aidl b/telephony/java/android/telephony/ims/DelegateRegistrationState.aidl new file mode 100644 index 0000000000000..756ea920d06a4 --- /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/DelegateRequest.aidl b/telephony/java/android/telephony/ims/DelegateRequest.aidl new file mode 100644 index 0000000000000..60c990f8258fe --- /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/FeatureTagState.aidl b/telephony/java/android/telephony/ims/FeatureTagState.aidl new file mode 100644 index 0000000000000..bce55742461c7 --- /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/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java index a4f2a316c99da..d1a893f61e000 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 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 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 94407f1dcd3a6..c3c3926fe8ea6 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.SubscriptionManager; import android.telephony.TelephonyFrameworkInitializer; @@ -152,14 +153,17 @@ public class ImsRcsManager { private final int mSubId; private final Context mContext; + private final BinderCacheManager 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 binderCache) { mSubId = subId; mContext = context; + mBinderCache = binderCache; } /** diff --git a/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.aidl b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.aidl new file mode 100644 index 0000000000000..44ae1b1300461 --- /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/SipDelegateManager.java b/telephony/java/android/telephony/ims/SipDelegateManager.java index 3c59d4f6209cd..337b7d49323d3 100644 --- a/telephony/java/android/telephony/ims/SipDelegateManager.java +++ b/telephony/java/android/telephony/ims/SipDelegateManager.java @@ -22,12 +22,12 @@ 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; @@ -261,6 +261,7 @@ public class SipDelegateManager { private final Context mContext; private final int mSubId; + private final BinderCacheManager mBinderCache; /** * Only visible for testing. To instantiate an instance of this class, please use @@ -268,9 +269,11 @@ public class SipDelegateManager { * @hide */ @VisibleForTesting - public SipDelegateManager(Context context, int subId) { + public SipDelegateManager(Context context, int subId, + BinderCacheManager binderCache) { mContext = context; mSubId = subId; + mBinderCache = binderCache; } /** @@ -290,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); @@ -315,7 +318,7 @@ public class SipDelegateManager { * 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 e The executor that will be used to call the callbacks associated with this + * @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 @@ -327,18 +330,34 @@ public class SipDelegateManager { * associated with this SipDelegateManager. See {@link ImsException#getCode()}. * @hide */ - public void createSipDelegate(@NonNull DelegateRequest request, @NonNull Executor e, + @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 || e == null || dc == null || mc == null) { + if (request == null || executor == null || dc == null || mc == null) { throw new IllegalArgumentException("Invalid arguments passed into createSipDelegate"); } - throw new ImsException("creating a SipDelegate is not supported" , - ImsException.CODE_ERROR_UNSUPPORTED_OPERATION); + 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 SipDelegateConnection that was created using + * Destroy a previously created {@link SipDelegateConnection} that was created using * {@link #createSipDelegate}. *

* This will also clean up all related callbacks in the associated ImsService. @@ -346,20 +365,32 @@ public class SipDelegateManager { * @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"); } - // not supported yet. - } - - private IImsRcsController getIImsRcsController() { - IBinder binder = TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getTelephonyImsServiceRegisterer() - .get(); - return IImsRcsController.Stub.asInterface(binder); + 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 0000000000000..5140f8ac357fb --- /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/aidl/IImsRcsController.aidl b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl index 6d25a09e079f4..f4e1a611792bf 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; @@ -55,6 +60,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 0000000000000..477ee958e1e83 --- /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 0000000000000..ddfcb9994cb82 --- /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 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 0000000000000..30b7d6c706478 --- /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/SipDelegateConnectionAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java new file mode 100644 index 0000000000000..3bd1a462b31af --- /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. + *

+ * 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 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 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)); + } +}