blob: 321bfff4065213910ce12385239508829555549a [file] [log] [blame]
/*
* 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.SystemApi;
import android.content.Context;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.telephony.ims.aidl.IImsConfig;
import android.telephony.ims.aidl.IImsConfigCallback;
import android.util.Log;
import com.android.ims.ImsConfig;
import com.android.internal.annotations.VisibleForTesting;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.HashMap;
/**
* 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<>();
@VisibleForTesting
public ImsConfigStub(ImsConfigImplBase imsConfigImplBase) {
mImsConfigImplBaseWeakReference =
new WeakReference<ImsConfigImplBase>(imsConfigImplBase);
}
@Override
public void addImsConfigCallback(IImsConfigCallback c) throws RemoteException {
getImsConfigImpl().addImsConfigCallback(c);
}
@Override
public void removeImsConfigCallback(IImsConfigCallback c) throws RemoteException {
getImsConfigImpl().removeImsConfigCallback(c);
}
/**
* 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 synchronized int getConfigInt(int item) throws RemoteException {
if (mProvisionedIntValue.containsKey(item)) {
return mProvisionedIntValue.get(item);
} else {
int retVal = getImsConfigImpl().getConfigInt(item);
if (retVal != ImsConfig.OperationStatusConstants.UNKNOWN) {
updateCachedValue(item, retVal, false);
}
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 synchronized String getConfigString(int item) throws RemoteException {
if (mProvisionedIntValue.containsKey(item)) {
return mProvisionedStringValue.get(item);
} else {
String retVal = getImsConfigImpl().getConfigString(item);
if (retVal != null) {
updateCachedValue(item, retVal, false);
}
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 master 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 synchronized int setConfigInt(int item, int value) throws RemoteException {
mProvisionedIntValue.remove(item);
int retVal = getImsConfigImpl().setConfig(item, value);
if (retVal == ImsConfig.OperationStatusConstants.SUCCESS) {
updateCachedValue(item, value, true);
} else {
Log.d(TAG, "Set provision value of " + item +
" to " + value + " failed with error code " + retVal);
}
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 master 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 synchronized int setConfigString(int item, String value)
throws RemoteException {
mProvisionedStringValue.remove(item);
int retVal = getImsConfigImpl().setConfig(item, value);
if (retVal == ImsConfig.OperationStatusConstants.SUCCESS) {
updateCachedValue(item, value, true);
}
return retVal;
}
private ImsConfigImplBase getImsConfigImpl() throws RemoteException {
ImsConfigImplBase ref = mImsConfigImplBaseWeakReference.get();
if (ref == null) {
throw new RemoteException("Fail to get ImsConfigImpl");
} else {
return ref;
}
}
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 synchronized void updateCachedValue(int item, int value, boolean notifyChange)
throws RemoteException {
mProvisionedIntValue.put(item, value);
if (notifyChange) {
notifyImsConfigChanged(item, value);
}
}
protected synchronized void updateCachedValue(int item, String value,
boolean notifyChange) throws RemoteException {
mProvisionedStringValue.put(item, value);
if (notifyChange) {
notifyImsConfigChanged(item, value);
}
}
}
/**
* The configuration requested resulted in an unknown result. This may happen if the
* IMS configurations are unavailable.
*/
public static final int CONFIG_RESULT_UNKNOWN = -1;
/**
* 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 RemoteCallbackList<IImsConfigCallback> mCallbacks = new RemoteCallbackList<>();
ImsConfigStub mImsConfigStub;
/**
* Used for compatibility between older versions of the ImsService.
* @hide
*/
public ImsConfigImplBase(Context context) {
mImsConfigStub = new ImsConfigStub(this);
}
public ImsConfigImplBase() {
mImsConfigStub = new ImsConfigStub(this);
}
/**
* 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;
}
mCallbacks.broadcast(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;
}
mCallbacks.broadcast(c -> {
try {
c.onStringConfigChanged(item, value);
} catch (RemoteException e) {
Log.w(TAG, "notifyConfigChanged(string): dead binder in notify, skipping.");
}
});
}
/**
* @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) {
try {
mImsConfigStub.updateCachedValue(item, value, true);
} 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) {
try {
mImsConfigStub.updateCachedValue(item, value, true);
} catch (RemoteException e) {
Log.w(TAG, "notifyProvisionedValueChanged(string): Framework connection is dead.");
}
}
/**
* 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;
}
}