| /* |
| * 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<ImsConfigImplBase> mImsConfigImplBaseWeakReference; |
| private HashMap<Integer, Integer> mProvisionedIntValue = new HashMap<>(); |
| private HashMap<Integer, String> 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>(imsConfigImplBase); |
| } |
| |
| @Override |
| public void addImsConfigCallback(IImsConfigCallback c) throws RemoteException { |
| AtomicReference<RemoteException> 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<RemoteException> 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<RemoteException> 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<RemoteException> 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<RemoteException> 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<RemoteException> 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<RemoteException> 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<RemoteException> 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<RemoteException> 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<RemoteException> 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<RemoteException> 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<RemoteException> 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<RemoteException> 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<RemoteException> 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<RemoteException> 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> T executeMethodAsyncForResult(Supplier<T> r, |
| String errorLogName) throws RemoteException { |
| CompletableFuture<T> 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<IImsConfigCallback> mCallbacks = |
| new RemoteCallbackListExt<>(); |
| private final RemoteCallbackListExt<IRcsConfigCallback> 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. |
| * |
| * <p>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; |
| } |
| } |
| } |