diff options
14 files changed, 559 insertions, 29 deletions
diff --git a/telephony/java/android/telephony/ImsManager.java b/telephony/java/android/telephony/ImsManager.java index 13f407b14f01..42d7707c97d9 100644 --- a/telephony/java/android/telephony/ImsManager.java +++ b/telephony/java/android/telephony/ImsManager.java @@ -22,7 +22,12 @@ import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.SystemService; import android.content.Context; +import android.telephony.BinderCacheManager; import android.telephony.SubscriptionManager; +import android.telephony.TelephonyFrameworkInitializer; +import android.telephony.ims.aidl.IImsRcsController; + +import com.android.internal.telephony.ITelephony; /** * Provides access to information about Telephony IMS services on the device. @@ -30,8 +35,6 @@ import android.telephony.SubscriptionManager; @SystemService(Context.TELEPHONY_IMS_SERVICE) public class ImsManager { - private Context mContext; - /** * <p>Broadcast Action: Indicates that a previously allowed IMS operation was rejected by the * network due to the network returning a "forbidden" response. This may be due to a @@ -87,6 +90,14 @@ public class ImsManager { public static final String EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE = "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_MESSAGE"; + // Cache Telephony Binder interfaces, one cache per process. + private static final BinderCacheManager<ITelephony> sTelephonyCache = + new BinderCacheManager<>(ImsManager::getITelephonyInterface); + private static final BinderCacheManager<IImsRcsController> sRcsCache = + new BinderCacheManager<>(ImsManager::getIImsRcsControllerInterface); + + private final Context mContext; + /** * Use {@link Context#getSystemService(String)} to get an instance of this class. * @hide @@ -108,7 +119,7 @@ public class ImsManager { throw new IllegalArgumentException("Invalid subscription ID: " + subscriptionId); } - return new ImsRcsManager(mContext, subscriptionId); + return new ImsRcsManager(mContext, subscriptionId, sRcsCache); } /** @@ -124,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 000000000000..756ea920d06a --- /dev/null +++ b/telephony/java/android/telephony/ims/DelegateRegistrationState.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims; + +parcelable DelegateRegistrationState; diff --git a/telephony/java/android/telephony/ims/DelegateRequest.aidl b/telephony/java/android/telephony/ims/DelegateRequest.aidl new file mode 100644 index 000000000000..60c990f8258f --- /dev/null +++ b/telephony/java/android/telephony/ims/DelegateRequest.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims; + +parcelable DelegateRequest; diff --git a/telephony/java/android/telephony/ims/FeatureTagState.aidl b/telephony/java/android/telephony/ims/FeatureTagState.aidl new file mode 100644 index 000000000000..bce55742461c --- /dev/null +++ b/telephony/java/android/telephony/ims/FeatureTagState.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims; + +parcelable FeatureTagState; diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java index a4f2a316c99d..d1a893f61e00 100644 --- a/telephony/java/android/telephony/ims/ImsMmTelManager.java +++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java @@ -29,6 +29,7 @@ import android.os.Binder; import android.os.RemoteException; import android.os.ServiceSpecificException; import android.telephony.AccessNetworkConstants; +import android.telephony.BinderCacheManager; import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionManager; import android.telephony.TelephonyFrameworkInitializer; @@ -213,6 +214,7 @@ public class ImsMmTelManager implements RegistrationManager { } private final int mSubId; + private final BinderCacheManager<ITelephony> mBinderCache; /** * Create an instance of {@link ImsMmTelManager} for the subscription id specified. @@ -242,7 +244,8 @@ public class ImsMmTelManager implements RegistrationManager { throw new IllegalArgumentException("Invalid subscription ID"); } - return new ImsMmTelManager(subId); + return new ImsMmTelManager(subId, new BinderCacheManager<>( + ImsMmTelManager::getITelephonyInterface)); } /** @@ -250,8 +253,9 @@ public class ImsMmTelManager implements RegistrationManager { * @hide */ @VisibleForTesting - public ImsMmTelManager(int subId) { + public ImsMmTelManager(int subId, BinderCacheManager<ITelephony> binderCache) { mSubId = subId; + mBinderCache = binderCache; } /** @@ -1367,7 +1371,11 @@ public class ImsMmTelManager implements RegistrationManager { } } - private static ITelephony getITelephony() { + private ITelephony getITelephony() { + return mBinderCache.getBinder(); + } + + private static ITelephony getITelephonyInterface() { ITelephony binder = ITelephony.Stub.asInterface( TelephonyFrameworkInitializer .getTelephonyServiceManager() diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java index 94407f1dcd3a..c3c3926fe8ea 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<IImsRcsController> mBinderCache; /** * Use {@link ImsManager#getImsRcsManager(int)} to create an instance of this class. * @hide */ - public ImsRcsManager(Context context, int subId) { + public ImsRcsManager(Context context, int subId, + BinderCacheManager<IImsRcsController> binderCache) { mSubId = subId; mContext = context; + mBinderCache = binderCache; } /** diff --git a/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.aidl b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.aidl new file mode 100644 index 000000000000..44ae1b130046 --- /dev/null +++ b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims; + +parcelable SipDelegateImsConfiguration; diff --git a/telephony/java/android/telephony/ims/SipDelegateManager.java b/telephony/java/android/telephony/ims/SipDelegateManager.java index 3c59d4f6209c..337b7d49323d 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<IImsRcsController> 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<IImsRcsController> 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}. * <p> * 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 000000000000..5140f8ac357f --- /dev/null +++ b/telephony/java/android/telephony/ims/SipMessage.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims; + +parcelable SipMessage; diff --git a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl index 6d25a09e079f..f4e1a611792b 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 000000000000..477ee958e1e8 --- /dev/null +++ b/telephony/java/android/telephony/ims/aidl/ISipDelegate.aidl @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims.aidl; + +import android.telephony.ims.SipMessage; + +/** + * See {@link SipDelegate} and {@link SipDelegateConnection} for docs regarding this callback. + * {@hide} + */ +oneway interface ISipDelegate { + void sendMessage(in SipMessage sipMessage, int configVersion); + void notifyMessageReceived(in String viaTransactionId); + void notifyMessageReceiveError(in String viaTransactionId, int reason); + + // only used by SipDelegate. + void closeDialog(in String callId); +} diff --git a/telephony/java/android/telephony/ims/aidl/ISipDelegateConnectionStateCallback.aidl b/telephony/java/android/telephony/ims/aidl/ISipDelegateConnectionStateCallback.aidl new file mode 100644 index 000000000000..ddfcb9994cb8 --- /dev/null +++ b/telephony/java/android/telephony/ims/aidl/ISipDelegateConnectionStateCallback.aidl @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims.aidl; + +import android.telephony.ims.DelegateRegistrationState; +import android.telephony.ims.FeatureTagState; +import android.telephony.ims.SipDelegateImsConfiguration; +import android.telephony.ims.aidl.ISipDelegate; + +/** + * See {@link SipDelegateConnectionStateCallback} for docs regarding this callback. + * {@hide} + */ +oneway interface ISipDelegateConnectionStateCallback { + void onCreated(ISipDelegate c); + void onFeatureTagStatusChanged(in DelegateRegistrationState registrationState, + in List<FeatureTagState> deniedFeatureTags); + void onImsConfigurationChanged(in SipDelegateImsConfiguration registeredSipConfig); + void onDestroyed(int reason); +} diff --git a/telephony/java/android/telephony/ims/aidl/ISipDelegateMessageCallback.aidl b/telephony/java/android/telephony/ims/aidl/ISipDelegateMessageCallback.aidl new file mode 100644 index 000000000000..30b7d6c70647 --- /dev/null +++ b/telephony/java/android/telephony/ims/aidl/ISipDelegateMessageCallback.aidl @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims.aidl; + +import android.telephony.ims.SipMessage; + +/** + * See {@link DelegateMessageCallback} and {@link DelegateConnectionMessageCallback} for docs + * regarding this callback. + * {@hide} + */ +oneway interface ISipDelegateMessageCallback { + void onMessageReceived(in SipMessage message); + void onMessageSent(in String viaTransactionId); + void onMessageSendFailure(in String viaTransactionId, int reason); +} diff --git a/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java new file mode 100644 index 000000000000..3bd1a462b31a --- /dev/null +++ b/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java @@ -0,0 +1,260 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims.aidl; + +import android.os.Binder; +import android.os.IBinder; +import android.os.RemoteException; +import android.telephony.ims.DelegateRegistrationState; +import android.telephony.ims.FeatureTagState; +import android.telephony.ims.SipDelegateConnection; +import android.telephony.ims.SipDelegateImsConfiguration; +import android.telephony.ims.SipDelegateManager; +import android.telephony.ims.SipMessage; +import android.telephony.ims.stub.DelegateConnectionMessageCallback; +import android.telephony.ims.stub.DelegateConnectionStateCallback; +import android.telephony.ims.stub.SipDelegate; +import android.util.ArraySet; +import android.util.Log; + +import java.util.List; +import java.util.NoSuchElementException; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicReference; + +/** + * Wrapper class implementing {@link SipDelegateConnection} using AIDL, which is returned to the + * local process. Also holds a reference to incoming connection message and state AIDL impl to + * trampoline events to callbacks as well as notify the local process in the event that the remote + * process becomes unavailable. + * <p> + * When the remote {@link SipDelegate} is created, this instance tracks the + * {@link ISipDelegate} associated with it and implements the + * {@link SipDelegateConnection} sent back to the local callback. + * @hide + */ +public class SipDelegateConnectionAidlWrapper implements SipDelegateConnection, + IBinder.DeathRecipient { + private static final String LOG_TAG = "SipDelegateCAW"; + + private final ISipDelegateConnectionStateCallback.Stub mStateBinder = + new ISipDelegateConnectionStateCallback.Stub() { + @Override + public void onCreated(ISipDelegate c) { + associateSipDelegate(c); + final long token = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> + mStateCallback.onCreated(SipDelegateConnectionAidlWrapper.this)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void onFeatureTagStatusChanged(DelegateRegistrationState registrationState, + List<FeatureTagState> deniedFeatureTags) { + final long token = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> + mStateCallback.onFeatureTagStatusChanged(registrationState, + new ArraySet<>(deniedFeatureTags))); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void onImsConfigurationChanged(SipDelegateImsConfiguration registeredSipConfig) { + final long token = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> + mStateCallback.onImsConfigurationChanged(registeredSipConfig)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void onDestroyed(int reason) { + invalidateSipDelegateBinder(); + final long token = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> + mStateCallback.onDestroyed(reason)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + }; + + private final ISipDelegateMessageCallback.Stub mMessageBinder = + new ISipDelegateMessageCallback.Stub() { + @Override + public void onMessageReceived(SipMessage message) { + final long token = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> + mMessageCallback.onMessageReceived(message)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void onMessageSent(String viaTransactionId) { + final long token = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> + mMessageCallback.onMessageSent(viaTransactionId)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void onMessageSendFailure(String viaTransactionId, int reason) { + final long token = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> + mMessageCallback.onMessageSendFailure(viaTransactionId, reason)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + }; + + + private final Executor mExecutor; + private final DelegateConnectionStateCallback mStateCallback; + private final DelegateConnectionMessageCallback mMessageCallback; + private final AtomicReference<ISipDelegate> mDelegateBinder = + new AtomicReference<>(); + + /** + * Wrap the local state and message callbacks, calling the implementation of these interfaces + * when the remote process calls these methods. + */ + public SipDelegateConnectionAidlWrapper(Executor executor, + DelegateConnectionStateCallback stateCallback, + DelegateConnectionMessageCallback messageCallback) { + mExecutor = executor; + mStateCallback = stateCallback; + mMessageCallback = messageCallback; + } + + @Override + public void sendMessage(SipMessage sipMessage, int configVersion) { + try { + ISipDelegate conn = getSipDelegateBinder(); + if (conn == null) { + notifyLocalMessageFailedToSend(sipMessage, + SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_CLOSED); + return; + } + conn.sendMessage(sipMessage, configVersion); + } catch (RemoteException e) { + notifyLocalMessageFailedToSend(sipMessage, + SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_DEAD); + } + } + + @Override + public void notifyMessageReceived(String viaTransactionId) { + try { + ISipDelegate conn = getSipDelegateBinder(); + if (conn == null) { + return; + } + conn.notifyMessageReceived(viaTransactionId); + } catch (RemoteException e) { + // Nothing to do here, app will eventually get remote death callback. + } + } + + @Override + public void notifyMessageReceiveError(String viaTransactionId, int reason) { + try { + ISipDelegate conn = getSipDelegateBinder(); + if (conn == null) { + return; + } + conn.notifyMessageReceiveError(viaTransactionId, reason); + } catch (RemoteException e) { + // Nothing to do here, app will eventually get remote death callback. + } + } + + // Also called upon IImsRcsController death (telephony process dies). + @Override + public void binderDied() { + invalidateSipDelegateBinder(); + mExecutor.execute(() -> mStateCallback.onDestroyed( + SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD)); + } + + /** + * @return Implementation of state binder. + */ + public ISipDelegateConnectionStateCallback getStateCallbackBinder() { + return mStateBinder; + } + + /** + * @return Implementation of message binder. + */ + public ISipDelegateMessageCallback getMessageCallbackBinder() { + return mMessageBinder; + } + + /** + * @return The ISipDelegateConnection associated with this wrapper. + */ + public ISipDelegate getSipDelegateBinder() { + return mDelegateBinder.get(); + } + + private void associateSipDelegate(ISipDelegate c) { + if (c != null) { + try { + c.asBinder().linkToDeath(this, 0 /*flags*/); + } catch (RemoteException e) { + // already dead. + c = null; + } + } + mDelegateBinder.set(c); + } + + private void invalidateSipDelegateBinder() { + ISipDelegate oldVal = mDelegateBinder.getAndUpdate((unused) -> null); + if (oldVal != null) { + try { + oldVal.asBinder().unlinkToDeath(this, 0 /*flags*/); + } catch (NoSuchElementException e) { + Log.i(LOG_TAG, "invalidateSipDelegateBinder: " + e); + } + } + } + + private void notifyLocalMessageFailedToSend(SipMessage m, int reason) { + //TODO: parse transaction ID or throw IllegalArgumentException if the SipMessage + // transaction ID can not be parsed. + mExecutor.execute(() -> + mMessageCallback.onMessageSendFailure(null, reason)); + } +} |