/* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License */ package android.telephony.ims.stub; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.SystemApi; import android.content.Context; import android.os.PersistableBundle; import android.os.RemoteException; import android.telephony.ims.ImsService; import android.telephony.ims.ProvisioningManager; import android.telephony.ims.RcsClientConfiguration; import android.telephony.ims.RcsConfig; import android.telephony.ims.aidl.IImsConfig; import android.telephony.ims.aidl.IImsConfigCallback; import android.telephony.ims.aidl.IRcsConfigCallback; import android.util.Log; import com.android.ims.ImsConfig; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.util.RemoteCallbackListExt; import com.android.internal.telephony.util.TelephonyUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; import java.util.Arrays; import java.util.HashMap; import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Supplier; /** * Controls the modification of IMS specific configurations. For more information on the supported * IMS configuration constants, see {@link ImsConfig}. * * The inner class {@link ImsConfigStub} implements methods of IImsConfig AIDL interface. * The IImsConfig AIDL interface is called by ImsConfig, which may exist in many other processes. * ImsConfigImpl access to the configuration parameters may be arbitrarily slow, especially in * during initialization, or times when a lot of configuration parameters are being set/get * (such as during boot up or SIM card change). By providing a cache in ImsConfigStub, we can speed * up access to these configuration parameters, so a query to the ImsConfigImpl does not have to be * performed every time. * @hide */ @SystemApi public class ImsConfigImplBase { private static final String TAG = "ImsConfigImplBase"; /** * Implements the IImsConfig AIDL interface, which is called by potentially many processes * in order to get/set configuration parameters. * * It holds an object of ImsConfigImplBase class which is usually extended by ImsConfigImpl * with actual implementations from vendors. This class caches provisioned values from * ImsConfigImpl layer because queries through ImsConfigImpl can be slow. When query goes in, * it first checks cache layer. If missed, it will call the vendor implementation of * ImsConfigImplBase API. * and cache the return value if the set succeeds. * * Provides APIs to get/set the IMS service feature/capability/parameters. * The config items include: * 1) Items provisioned by the operator. * 2) Items configured by user. Mainly service feature class. * * @hide */ @VisibleForTesting static public class ImsConfigStub extends IImsConfig.Stub { WeakReference mImsConfigImplBaseWeakReference; private HashMap mProvisionedIntValue = new HashMap<>(); private HashMap mProvisionedStringValue = new HashMap<>(); private final Object mLock = new Object(); private Executor mExecutor; @VisibleForTesting public ImsConfigStub(ImsConfigImplBase imsConfigImplBase, Executor executor) { mExecutor = executor; mImsConfigImplBaseWeakReference = new WeakReference(imsConfigImplBase); } @Override public void addImsConfigCallback(IImsConfigCallback c) throws RemoteException { AtomicReference exceptionRef = new AtomicReference<>(); executeMethodAsync(()-> { try { getImsConfigImpl().addImsConfigCallback(c); } catch (RemoteException e) { exceptionRef.set(e); } }, "addImsConfigCallback"); if (exceptionRef.get() != null) { Log.d(TAG, "ImsConfigImplBase Exception addImsConfigCallback"); throw exceptionRef.get(); } } @Override public void removeImsConfigCallback(IImsConfigCallback c) throws RemoteException { AtomicReference exceptionRef = new AtomicReference<>(); executeMethodAsync(()-> { try { getImsConfigImpl().removeImsConfigCallback(c); } catch (RemoteException e) { exceptionRef.set(e); } }, "removeImsConfigCallback"); if (exceptionRef.get() != null) { Log.d(TAG, "ImsConfigImplBase Exception removeImsConfigCallback"); throw exceptionRef.get(); } } /** * Gets the value for ims service/capabilities parameters. It first checks its local cache, * if missed, it will call ImsConfigImplBase.getConfigInt. * Synchronous blocking call. * * @param item integer key * @return value in Integer format or {@link #CONFIG_RESULT_UNKNOWN} if * unavailable. */ @Override public int getConfigInt(int item) throws RemoteException { AtomicReference exceptionRef = new AtomicReference<>(); int retVal = executeMethodAsyncForResult(()-> { int returnVal = ImsConfig.OperationStatusConstants.UNKNOWN; synchronized (mLock) { if (mProvisionedIntValue.containsKey(item)) { return mProvisionedIntValue.get(item); } else { try { returnVal = getImsConfigImpl().getConfigInt(item); if (returnVal != ImsConfig.OperationStatusConstants.UNKNOWN) { mProvisionedIntValue.put(item, returnVal); } } catch (RemoteException e) { exceptionRef.set(e); return returnVal; } } } return returnVal; }, "getConfigInt"); if (exceptionRef.get() != null) { Log.d(TAG, "ImsConfigImplBase Exception getConfigString"); throw exceptionRef.get(); } return retVal; } /** * Gets the value for ims service/capabilities parameters. It first checks its local cache, * if missed, it will call #ImsConfigImplBase.getConfigString. * Synchronous blocking call. * * @param item integer key * @return value in String format. */ @Override public String getConfigString(int item) throws RemoteException { AtomicReference exceptionRef = new AtomicReference<>(); String retVal = executeMethodAsyncForResult(()-> { String returnVal = null; synchronized (mLock) { if (mProvisionedStringValue.containsKey(item)) { returnVal = mProvisionedStringValue.get(item); } else { try { returnVal = getImsConfigImpl().getConfigString(item); if (returnVal != null) { mProvisionedStringValue.put(item, returnVal); } } catch (RemoteException e) { exceptionRef.set(e); return returnVal; } } } return returnVal; }, "getConfigString"); if (exceptionRef.get() != null) { Log.d(TAG, "ImsConfigImplBase Exception getConfigString"); throw exceptionRef.get(); } return retVal; } /** * Sets the value for IMS service/capabilities parameters by the operator device * management entity. It sets the config item value in the provisioned storage * from which the main value is derived, and write it into local cache. * Synchronous blocking call. * * @param item integer key * @param value in Integer format. * @return the result of setting the configuration value, defined as either * {@link #CONFIG_RESULT_FAILED} or {@link #CONFIG_RESULT_SUCCESS}. */ @Override public int setConfigInt(int item, int value) throws RemoteException { AtomicReference exceptionRef = new AtomicReference<>(); int retVal = executeMethodAsyncForResult(()-> { int returnVal = ImsConfig.OperationStatusConstants.UNKNOWN; try { synchronized (mLock) { mProvisionedIntValue.remove(item); returnVal = getImsConfigImpl().setConfig(item, value); if (returnVal == ImsConfig.OperationStatusConstants.SUCCESS) { mProvisionedIntValue.put(item, value); } else { Log.d(TAG, "Set provision value of " + item + " to " + value + " failed with error code " + returnVal); } } notifyImsConfigChanged(item, value); return returnVal; } catch (RemoteException e) { exceptionRef.set(e); return returnVal; } }, "setConfigInt"); if (exceptionRef.get() != null) { Log.d(TAG, "ImsConfigImplBase Exception setConfigInt"); throw exceptionRef.get(); } return retVal; } /** * Sets the value for IMS service/capabilities parameters by the operator device * management entity. It sets the config item value in the provisioned storage * from which the main value is derived, and write it into local cache. * Synchronous blocking call. * * @param item as defined in com.android.ims.ImsConfig#ConfigConstants. * @param value in String format. * @return the result of setting the configuration value, defined as either * {@link #CONFIG_RESULT_FAILED} or {@link #CONFIG_RESULT_SUCCESS}. */ @Override public int setConfigString(int item, String value) throws RemoteException { AtomicReference exceptionRef = new AtomicReference<>(); int retVal = executeMethodAsyncForResult(()-> { int returnVal = ImsConfig.OperationStatusConstants.UNKNOWN; try { synchronized (mLock) { mProvisionedStringValue.remove(item); returnVal = getImsConfigImpl().setConfig(item, value); if (returnVal == ImsConfig.OperationStatusConstants.SUCCESS) { mProvisionedStringValue.put(item, value); } } notifyImsConfigChanged(item, value); return returnVal; } catch (RemoteException e) { exceptionRef.set(e); return returnVal; } }, "setConfigString"); if (exceptionRef.get() != null) { Log.d(TAG, "ImsConfigImplBase Exception setConfigInt"); throw exceptionRef.get(); } return retVal; } @Override public void updateImsCarrierConfigs(PersistableBundle bundle) throws RemoteException { AtomicReference exceptionRef = new AtomicReference<>(); executeMethodAsync(()-> { try { getImsConfigImpl().updateImsCarrierConfigs(bundle); } catch (RemoteException e) { exceptionRef.set(e); } }, "updateImsCarrierConfigs"); if (exceptionRef.get() != null) { Log.d(TAG, "ImsConfigImplBase Exception updateImsCarrierConfigs"); throw exceptionRef.get(); } } private ImsConfigImplBase getImsConfigImpl() throws RemoteException { ImsConfigImplBase ref = mImsConfigImplBaseWeakReference.get(); if (ref == null) { throw new RemoteException("Fail to get ImsConfigImpl"); } else { return ref; } } @Override public void notifyRcsAutoConfigurationReceived(byte[] config, boolean isCompressed) throws RemoteException { AtomicReference exceptionRef = new AtomicReference<>(); executeMethodAsync(()-> { try { getImsConfigImpl().onNotifyRcsAutoConfigurationReceived(config, isCompressed); } catch (RemoteException e) { exceptionRef.set(e); } }, "notifyRcsAutoConfigurationReceived"); if (exceptionRef.get() != null) { Log.d(TAG, "ImsConfigImplBase Exception notifyRcsAutoConfigurationReceived"); throw exceptionRef.get(); } } @Override public void notifyRcsAutoConfigurationRemoved() throws RemoteException { AtomicReference exceptionRef = new AtomicReference<>(); executeMethodAsync(()-> { try { getImsConfigImpl().onNotifyRcsAutoConfigurationRemoved(); } catch (RemoteException e) { exceptionRef.set(e); } }, "notifyRcsAutoConfigurationRemoved"); if (exceptionRef.get() != null) { Log.d(TAG, "ImsConfigImplBase Exception notifyRcsAutoConfigurationRemoved"); throw exceptionRef.get(); } } private void notifyImsConfigChanged(int item, int value) throws RemoteException { getImsConfigImpl().notifyConfigChanged(item, value); } private void notifyImsConfigChanged(int item, String value) throws RemoteException { getImsConfigImpl().notifyConfigChanged(item, value); } protected void updateCachedValue(int item, int value) { synchronized (mLock) { mProvisionedIntValue.put(item, value); } } protected void updateCachedValue(int item, String value) { synchronized (mLock) { mProvisionedStringValue.put(item, value); } } @Override public void addRcsConfigCallback(IRcsConfigCallback c) throws RemoteException { AtomicReference exceptionRef = new AtomicReference<>(); executeMethodAsync(()-> { try { getImsConfigImpl().addRcsConfigCallback(c); } catch (RemoteException e) { exceptionRef.set(e); } }, "addRcsConfigCallback"); if (exceptionRef.get() != null) { Log.d(TAG, "ImsConfigImplBase Exception addRcsConfigCallback"); throw exceptionRef.get(); } } @Override public void removeRcsConfigCallback(IRcsConfigCallback c) throws RemoteException { AtomicReference exceptionRef = new AtomicReference<>(); executeMethodAsync(()-> { try { getImsConfigImpl().removeRcsConfigCallback(c); } catch (RemoteException e) { exceptionRef.set(e); } }, "removeRcsConfigCallback"); if (exceptionRef.get() != null) { Log.d(TAG, "ImsConfigImplBase Exception removeRcsConfigCallback"); throw exceptionRef.get(); } } @Override public void triggerRcsReconfiguration() throws RemoteException { AtomicReference exceptionRef = new AtomicReference<>(); executeMethodAsync(()-> { try { getImsConfigImpl().triggerAutoConfiguration(); } catch (RemoteException e) { exceptionRef.set(e); } }, "triggerRcsReconfiguration"); if (exceptionRef.get() != null) { Log.d(TAG, "ImsConfigImplBase Exception triggerRcsReconfiguration"); throw exceptionRef.get(); } } @Override public void setRcsClientConfiguration(RcsClientConfiguration rcc) throws RemoteException { AtomicReference exceptionRef = new AtomicReference<>(); executeMethodAsync(()-> { try { getImsConfigImpl().setRcsClientConfiguration(rcc); } catch (RemoteException e) { exceptionRef.set(e); } }, "setRcsClientConfiguration"); if (exceptionRef.get() != null) { Log.d(TAG, "ImsConfigImplBase Exception setRcsClientConfiguration"); throw exceptionRef.get(); } } @Override public void notifyIntImsConfigChanged(int item, int value) throws RemoteException { AtomicReference exceptionRef = new AtomicReference<>(); executeMethodAsync(()-> { try { notifyImsConfigChanged(item, value); } catch (RemoteException e) { exceptionRef.set(e); } }, "notifyIntImsConfigChanged"); if (exceptionRef.get() != null) { Log.d(TAG, "ImsConfigImplBase Exception notifyIntImsConfigChanged"); throw exceptionRef.get(); } } @Override public void notifyStringImsConfigChanged(int item, String value) throws RemoteException { AtomicReference exceptionRef = new AtomicReference<>(); executeMethodAsync(()-> { try { notifyImsConfigChanged(item, value); } catch (RemoteException e) { exceptionRef.set(e); } }, "notifyStringImsConfigChanged"); if (exceptionRef.get() != null) { Log.d(TAG, "ImsConfigImplBase Exception notifyStringImsConfigChanged"); throw exceptionRef.get(); } } /** * Clear cached configuration value. */ public void clearCachedValue() { Log.i(TAG, "clearCachedValue"); synchronized (mLock) { mProvisionedIntValue.clear(); mProvisionedStringValue.clear(); } } // Call the methods with a clean calling identity on the executor and wait indefinitely for // the future to return. private void executeMethodAsync(Runnable r, String errorLogName) throws RemoteException { try { CompletableFuture.runAsync( () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join(); } catch (CancellationException | CompletionException e) { Log.w(TAG, "ImsConfigImplBase Binder - " + errorLogName + " exception: " + e.getMessage()); throw new RemoteException(e.getMessage()); } } private T executeMethodAsyncForResult(Supplier r, String errorLogName) throws RemoteException { CompletableFuture future = CompletableFuture.supplyAsync( () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor); try { return future.get(); } catch (ExecutionException | InterruptedException e) { Log.w(TAG, "ImsConfigImplBase Binder - " + errorLogName + " exception: " + e.getMessage()); throw new RemoteException(e.getMessage()); } } } /** * The configuration requested resulted in an unknown result. This may happen if the * IMS configurations are unavailable. */ public static final int CONFIG_RESULT_UNKNOWN = ProvisioningManager.PROVISIONING_RESULT_UNKNOWN; /** * Setting the configuration value completed. */ public static final int CONFIG_RESULT_SUCCESS = 0; /** * Setting the configuration value failed. */ public static final int CONFIG_RESULT_FAILED = 1; /** * @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = "CONFIG_RESULT_", value = { CONFIG_RESULT_SUCCESS, CONFIG_RESULT_FAILED }) public @interface SetConfigResult {} private final RemoteCallbackListExt mCallbacks = new RemoteCallbackListExt<>(); private final RemoteCallbackListExt mRcsCallbacks = new RemoteCallbackListExt<>(); private byte[] mRcsConfigData; private final Object mRcsConfigDataLock = new Object(); ImsConfigStub mImsConfigStub; /** * Create an ImsConfig using the Executor specified for methods being called by the * framework. * @param executor The executor for the framework to use when executing the methods overridden * by the implementation of ImsConfig. */ public ImsConfigImplBase(@NonNull Executor executor) { mImsConfigStub = new ImsConfigStub(this, executor); } /** * @hide */ public ImsConfigImplBase(@NonNull Context context) { mImsConfigStub = new ImsConfigStub(this, null); } /** * Create an ImsConfig using the Executor defined in {@link ImsService#getExecutor} */ public ImsConfigImplBase() { mImsConfigStub = new ImsConfigStub(this, null); } /** * Adds a {@link android.telephony.ims.ProvisioningManager.Callback} to the list of callbacks * notified when a value in the configuration changes. * @param c callback to add. */ private void addImsConfigCallback(IImsConfigCallback c) { mCallbacks.register(c); } /** * Removes a {@link android.telephony.ims.ProvisioningManager.Callback} to the list of callbacks * notified when a value in the configuration changes. * @param c callback to remove. */ private void removeImsConfigCallback(IImsConfigCallback c) { mCallbacks.unregister(c); } /** * @param item * @param value */ private final void notifyConfigChanged(int item, int value) { // can be null in testing if (mCallbacks == null) { return; } synchronized (mCallbacks) { mCallbacks.broadcastAction(c -> { try { c.onIntConfigChanged(item, value); } catch (RemoteException e) { Log.w(TAG, "notifyConfigChanged(int): dead binder in notify, skipping."); } }); } } private void notifyConfigChanged(int item, String value) { // can be null in testing if (mCallbacks == null) { return; } synchronized (mCallbacks) { mCallbacks.broadcastAction(c -> { try { c.onStringConfigChanged(item, value); } catch (RemoteException e) { Log.w(TAG, "notifyConfigChanged(string): dead binder in notify, skipping."); } }); } } private void addRcsConfigCallback(IRcsConfigCallback c) { mRcsCallbacks.register(c); // This is used to avoid calling the binder out of the synchronized scope. byte[] cloneRcsConfigData; synchronized (mRcsConfigDataLock) { if (mRcsConfigData == null) { return; } cloneRcsConfigData = mRcsConfigData.clone(); } try { c.onConfigurationChanged(cloneRcsConfigData); } catch (RemoteException e) { Log.w(TAG, "dead binder to call onConfigurationChanged, skipping."); } } private void removeRcsConfigCallback(IRcsConfigCallback c) { mRcsCallbacks.unregister(c); } private void onNotifyRcsAutoConfigurationReceived(byte[] config, boolean isCompressed) { // cache uncompressed config final byte[] rcsConfigData = isCompressed ? RcsConfig.decompressGzip(config) : config; synchronized (mRcsConfigDataLock) { if (Arrays.equals(mRcsConfigData, config)) { return; } mRcsConfigData = rcsConfigData; } // can be null in testing if (mRcsCallbacks != null) { synchronized (mRcsCallbacks) { mRcsCallbacks.broadcastAction(c -> { try { // config is cloned here so modifications to the config passed to the // vendor do not accidentally modify the cache. c.onConfigurationChanged(rcsConfigData.clone()); } catch (RemoteException e) { Log.w(TAG, "dead binder in notifyRcsAutoConfigurationReceived, skipping."); } }); } } notifyRcsAutoConfigurationReceived(config, isCompressed); } private void onNotifyRcsAutoConfigurationRemoved() { synchronized (mRcsConfigDataLock) { mRcsConfigData = null; } if (mRcsCallbacks != null) { synchronized (mRcsCallbacks) { mRcsCallbacks.broadcastAction(c -> { try { c.onConfigurationReset(); } catch (RemoteException e) { Log.w(TAG, "dead binder in notifyRcsAutoConfigurationRemoved, skipping."); } }); } } notifyRcsAutoConfigurationRemoved(); } /** * @hide */ public IImsConfig getIImsConfig() { return mImsConfigStub; } /** * Updates provisioning value and notifies the framework of the change. * Doesn't call {@link #setConfig(int,int)} and assumes the result succeeded. * This should only be used when the IMS implementer implicitly changed provisioned values. * * @param item an integer key. * @param value in Integer format. */ public final void notifyProvisionedValueChanged(int item, int value) { mImsConfigStub.updateCachedValue(item, value); try { mImsConfigStub.notifyImsConfigChanged(item, value); } catch (RemoteException e) { Log.w(TAG, "notifyProvisionedValueChanged(int): Framework connection is dead."); } } /** * Updates provisioning value and notifies the framework of the change. * Doesn't call {@link #setConfig(int,String)} and assumes the result succeeded. * This should only be used when the IMS implementer implicitly changed provisioned values. * * @param item an integer key. * @param value in String format. */ public final void notifyProvisionedValueChanged(int item, String value) { mImsConfigStub.updateCachedValue(item, value); try { mImsConfigStub.notifyImsConfigChanged(item, value); } catch (RemoteException e) { Log.w(TAG, "notifyProvisionedValueChanged(string): Framework connection is dead."); } } /** * The framework has received an RCS autoconfiguration XML file for provisioning. * * @param config The XML file to be read, if not compressed, it should be in ASCII/UTF8 format. * @param isCompressed The XML file is compressed in gzip format and must be decompressed * before being read. * */ public void notifyRcsAutoConfigurationReceived(@NonNull byte[] config, boolean isCompressed) { } /** * The RCS autoconfiguration XML file is removed or invalid. */ public void notifyRcsAutoConfigurationRemoved() { } /** * Sets the configuration value for this ImsService. * * @param item an integer key. * @param value an integer containing the configuration value. * @return the result of setting the configuration value. */ public @SetConfigResult int setConfig(int item, int value) { // Base Implementation - To be overridden. return CONFIG_RESULT_FAILED; } /** * Sets the configuration value for this ImsService. * * @param item an integer key. * @param value a String containing the new configuration value. * @return Result of setting the configuration value. */ public @SetConfigResult int setConfig(int item, String value) { // Base Implementation - To be overridden. return CONFIG_RESULT_FAILED; } /** * Gets the currently stored value configuration value from the ImsService for {@code item}. * * @param item an integer key. * @return configuration value, stored in integer format or {@link #CONFIG_RESULT_UNKNOWN} if * unavailable. */ public int getConfigInt(int item) { // Base Implementation - To be overridden. return CONFIG_RESULT_UNKNOWN; } /** * Gets the currently stored value configuration value from the ImsService for {@code item}. * * @param item an integer key. * @return configuration value, stored in String format or {@code null} if unavailable. */ public String getConfigString(int item) { // Base Implementation - To be overridden. return null; } /** * @hide */ public void updateImsCarrierConfigs(PersistableBundle bundle) { // Base Implementation - Should be overridden } /** * Default messaging application parameters are sent to the ACS client * using this interface. * @param rcc RCS client configuration {@link RcsClientConfiguration} */ public void setRcsClientConfiguration(@NonNull RcsClientConfiguration rcc) { // Base Implementation - Should be overridden } /** * Reconfiguration triggered by the RCS application. Most likely cause * is the 403 forbidden to a SIP/HTTP request */ public void triggerAutoConfiguration() { // Base Implementation - Should be overridden } /** * Errors during autoconfiguration connection setup are notified by the * ACS client using this interface. * @param errorCode HTTP error received during connection setup. * @param errorString reason phrase received with the error */ public final void notifyAutoConfigurationErrorReceived(int errorCode, @NonNull String errorString) { // can be null in testing if (mRcsCallbacks == null) { return; } synchronized (mRcsCallbacks) { mRcsCallbacks.broadcastAction(c -> { try { c.onAutoConfigurationErrorReceived(errorCode, errorString); } catch (RemoteException e) { Log.w(TAG, "dead binder in notifyAutoConfigurationErrorReceived, skipping."); } }); } } /** * Notifies application that pre-provisioning config is received. * *

Some carriers using ACS (auto configuration server) may send a carrier-specific * pre-provisioning configuration XML if the user has not been provisioned for RCS * services yet. When such provisioning XML is received, ACS client must call this * method to notify the application with the XML. * * @param configXml the pre-provisioning config in carrier specified format. */ public final void notifyPreProvisioningReceived(@NonNull byte[] configXml) { // can be null in testing if (mRcsCallbacks == null) { return; } synchronized (mRcsCallbacks) { mRcsCallbacks.broadcastAction(c -> { try { c.onPreProvisioningReceived(configXml); } catch (RemoteException e) { Log.w(TAG, "dead binder in notifyPreProvisioningReceived, skipping."); } }); } } /** * Set default Executor from ImsService. * @param executor The default executor for the framework to use when executing the methods * overridden by the implementation of ImsConfig. * @hide */ public final void setDefaultExecutor(@NonNull Executor executor) { if (mImsConfigStub.mExecutor == null) { mImsConfigStub.mExecutor = executor; } } /** * Clear all cached config data. This will be called when the config data is no longer valid * such as when the SIM was removed. * @hide */ public final void clearConfigurationCache() { mImsConfigStub.clearCachedValue(); synchronized (mRcsConfigDataLock) { mRcsConfigData = null; } } }