blob: e819afcb8c738efbc8469d97636f420f3152bd40 [file] [log] [blame]
/*
* Copyright 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 com.android.phone;
import static com.android.internal.telephony.TelephonyStatsLog.RCS_ACS_PROVISIONING_STATS__RESPONSE_TYPE__PROVISIONING_XML;
import static com.android.internal.telephony.TelephonyStatsLog.RCS_CLIENT_PROVISIONING_STATS__EVENT__DMA_CHANGED;
import static com.android.internal.telephony.TelephonyStatsLog.RCS_CLIENT_PROVISIONING_STATS__EVENT__TRIGGER_RCS_RECONFIGURATION;
import android.Manifest;
import android.app.role.OnRoleHoldersChangedListener;
import android.app.role.RoleManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.UserHandle;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyRegistryManager;
import android.telephony.ims.ProvisioningManager;
import android.telephony.ims.RcsConfig;
import android.telephony.ims.aidl.IImsConfig;
import android.telephony.ims.aidl.IRcsConfigCallback;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.SparseArray;
import com.android.ims.FeatureConnector;
import com.android.ims.FeatureUpdates;
import com.android.ims.RcsFeatureManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.metrics.RcsStats;
import com.android.internal.telephony.metrics.RcsStats.RcsProvisioningCallback;
import com.android.internal.telephony.util.HandlerExecutor;
import com.android.internal.util.CollectionUtils;
import com.android.telephony.Rlog;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
/**
* Class to monitor RCS Provisioning Status
*/
public class RcsProvisioningMonitor {
private static final String TAG = "RcsProvisioningMonitor";
private static final boolean DBG = Build.IS_ENG;
private static final int EVENT_SUB_CHANGED = 1;
private static final int EVENT_DMA_CHANGED = 2;
private static final int EVENT_CC_CHANGED = 3;
private static final int EVENT_CONFIG_RECEIVED = 4;
private static final int EVENT_RECONFIG_REQUEST = 5;
private static final int EVENT_DEVICE_CONFIG_OVERRIDE = 6;
private static final int EVENT_CARRIER_CONFIG_OVERRIDE = 7;
private static final int EVENT_RESET = 8;
private static final int EVENT_FEATURE_ENABLED_OVERRIDE = 9;
// indicate that the carrier single registration capable is initial value as
// carrier config is not ready yet.
private static final int MASK_CAP_CARRIER_INIT = 0xF000;
private final PhoneGlobals mPhone;
private final Handler mHandler;
// Cache the RCS provsioning info and related sub id
private final ConcurrentHashMap<Integer, RcsProvisioningInfo> mRcsProvisioningInfos =
new ConcurrentHashMap<>();
private Boolean mDeviceSingleRegistrationEnabledOverride;
private final HashMap<Integer, Boolean> mCarrierSingleRegistrationEnabledOverride =
new HashMap<>();
private final ConcurrentHashMap<Integer, Boolean> mImsFeatureValidationOverride =
new ConcurrentHashMap<>();
private String mDmaPackageName;
private final SparseArray<RcsFeatureListener> mRcsFeatureListeners = new SparseArray<>();
private volatile boolean mTestModeEnabled;
private final CarrierConfigManager mCarrierConfigManager;
private final DmaChangedListener mDmaChangedListener;
private final SubscriptionManager mSubscriptionManager;
private final TelephonyRegistryManager mTelephonyRegistryManager;
private final RoleManagerAdapter mRoleManager;
private FeatureConnectorFactory<RcsFeatureManager> mFeatureFactory;
private RcsStats mRcsStats;
private static RcsProvisioningMonitor sInstance;
private final SubscriptionManager.OnSubscriptionsChangedListener mSubChangedListener =
new SubscriptionManager.OnSubscriptionsChangedListener() {
@Override
public void onSubscriptionsChanged() {
if (!mHandler.hasMessages(EVENT_SUB_CHANGED)) {
mHandler.sendEmptyMessage(EVENT_SUB_CHANGED);
}
}
};
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(
intent.getAction())) {
int subId = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
logv("Carrier-config changed for sub : " + subId);
if (SubscriptionManager.isValidSubscriptionId(subId)
&& !mHandler.hasMessages(EVENT_CC_CHANGED)) {
mHandler.sendEmptyMessage(EVENT_CC_CHANGED);
}
}
}
};
private final class DmaChangedListener implements OnRoleHoldersChangedListener {
@Override
public void onRoleHoldersChanged(String role, UserHandle user) {
if (RoleManager.ROLE_SMS.equals(role)) {
logv("default messaging application changed.");
mHandler.sendEmptyMessage(EVENT_DMA_CHANGED);
}
}
public void register() {
try {
mRoleManager.addOnRoleHoldersChangedListenerAsUser(
mPhone.getMainExecutor(), this, UserHandle.SYSTEM);
} catch (RuntimeException e) {
loge("Could not register dma change listener due to " + e);
}
}
public void unregister() {
try {
mRoleManager.removeOnRoleHoldersChangedListenerAsUser(this, UserHandle.SYSTEM);
} catch (RuntimeException e) {
loge("Could not unregister dma change listener due to " + e);
}
}
}
private final class MyHandler extends Handler {
MyHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
logv("handleMessage: " + msg);
switch (msg.what) {
case EVENT_SUB_CHANGED:
onSubChanged();
break;
case EVENT_DMA_CHANGED:
onDefaultMessagingApplicationChanged();
break;
case EVENT_CC_CHANGED:
onCarrierConfigChange();
break;
case EVENT_CONFIG_RECEIVED:
onConfigReceived(msg.arg1, (byte[]) msg.obj, msg.arg2 == 1);
break;
case EVENT_RECONFIG_REQUEST:
onReconfigRequest(msg.arg1);
break;
case EVENT_DEVICE_CONFIG_OVERRIDE:
Boolean deviceEnabled = (Boolean) msg.obj;
if (!booleanEquals(deviceEnabled, mDeviceSingleRegistrationEnabledOverride)) {
mDeviceSingleRegistrationEnabledOverride = deviceEnabled;
onCarrierConfigChange();
}
break;
case EVENT_CARRIER_CONFIG_OVERRIDE:
Boolean carrierEnabledOverride = (Boolean) msg.obj;
Boolean carrierEnabled = mCarrierSingleRegistrationEnabledOverride.put(
msg.arg1, carrierEnabledOverride);
if (!booleanEquals(carrierEnabledOverride, carrierEnabled)) {
onCarrierConfigChange();
}
break;
case EVENT_RESET:
reset();
break;
default:
loge("Unhandled event " + msg.what);
}
}
}
private final class RcsProvisioningInfo {
private int mSubId;
private volatile int mSingleRegistrationCapability;
private volatile byte[] mConfig;
private ArraySet<IRcsConfigCallback> mRcsConfigCallbacks;
private IImsConfig mIImsConfig;
private boolean mHasReconfigRequest;
RcsProvisioningInfo(int subId, int singleRegistrationCapability, byte[] config) {
mSubId = subId;
mSingleRegistrationCapability = singleRegistrationCapability;
mConfig = config;
mRcsConfigCallbacks = new ArraySet<>();
registerRcsFeatureListener(this);
}
int getSubId() {
return mSubId;
}
void setSingleRegistrationCapability(int singleRegistrationCapability) {
if (mSingleRegistrationCapability != singleRegistrationCapability) {
mSingleRegistrationCapability = singleRegistrationCapability;
notifyDma();
// update whether single registration supported.
mRcsStats.setEnableSingleRegistration(mSubId,
mSingleRegistrationCapability == ProvisioningManager.STATUS_CAPABLE);
}
}
void notifyDma() {
// notify only if capable value has been updated when carrier config ready.
if ((mSingleRegistrationCapability & MASK_CAP_CARRIER_INIT) != MASK_CAP_CARRIER_INIT) {
logi("notify default messaging app for sub:" + mSubId + " with capability:"
+ mSingleRegistrationCapability);
notifyDmaForSub(mSubId, mSingleRegistrationCapability);
}
}
int getSingleRegistrationCapability() {
return mSingleRegistrationCapability;
}
void setConfig(byte[] config) {
if (!Arrays.equals(mConfig, config)) {
mConfig = config;
if (mConfig != null) {
notifyRcsAutoConfigurationReceived();
} else {
notifyRcsAutoConfigurationRemoved();
}
}
}
byte[] getConfig() {
return mConfig;
}
boolean addRcsConfigCallback(IRcsConfigCallback cb) {
if (mIImsConfig == null) {
logd("fail to addRcsConfigCallback as imsConfig is null");
return false;
}
synchronized (mRcsConfigCallbacks) {
try {
mIImsConfig.addRcsConfigCallback(cb);
} catch (RemoteException e) {
loge("fail to addRcsConfigCallback due to " + e);
return false;
}
mRcsConfigCallbacks.add(cb);
}
return true;
}
boolean removeRcsConfigCallback(IRcsConfigCallback cb) {
boolean result = true;
synchronized (mRcsConfigCallbacks) {
if (mIImsConfig != null) {
try {
mIImsConfig.removeRcsConfigCallback(cb);
} catch (RemoteException e) {
loge("fail to removeRcsConfigCallback due to " + e);
}
} else {
// Return false but continue to remove the callback
result = false;
}
try {
cb.onRemoved();
} catch (RemoteException e) {
logd("Failed to notify onRemoved due to dead binder of " + cb);
}
mRcsConfigCallbacks.remove(cb);
}
return result;
}
void triggerRcsReconfiguration() {
if (mIImsConfig != null) {
try {
logv("triggerRcsReconfiguration for sub:" + mSubId);
mIImsConfig.triggerRcsReconfiguration();
mHasReconfigRequest = false;
} catch (RemoteException e) {
loge("triggerRcsReconfiguration failed due to " + e);
}
} else {
logd("triggerRcsReconfiguration failed due to IImsConfig null.");
mHasReconfigRequest = true;
}
}
void destroy() {
unregisterRcsFeatureListener(this);
clear();
mIImsConfig = null;
mRcsConfigCallbacks = null;
}
void clear() {
setConfig(null);
clearCallbacks();
}
void onRcsStatusChanged(IImsConfig binder) {
logv("onRcsStatusChanged for sub:" + mSubId + ", IImsConfig?" + binder);
if (mIImsConfig != binder) {
mIImsConfig = binder;
if (mIImsConfig != null) {
if (mHasReconfigRequest) {
triggerRcsReconfiguration();
} else {
notifyRcsAutoConfigurationReceived();
}
// check callback for metrics if not registered, register callback
registerMetricsCallback();
} else {
// clear callbacks if rcs disconnected
clearCallbacks();
}
}
}
private void notifyRcsAutoConfigurationReceived() {
if (mConfig == null) {
logd("Rcs config is null for sub : " + mSubId);
return;
}
if (mIImsConfig != null) {
try {
logv("notifyRcsAutoConfigurationReceived for sub:" + mSubId);
mIImsConfig.notifyRcsAutoConfigurationReceived(mConfig, false);
} catch (RemoteException e) {
loge("notifyRcsAutoConfigurationReceived failed due to " + e);
}
} else {
logd("notifyRcsAutoConfigurationReceived failed due to IImsConfig null.");
}
}
private void notifyRcsAutoConfigurationRemoved() {
if (mIImsConfig != null) {
try {
logv("notifyRcsAutoConfigurationRemoved for sub:" + mSubId);
mIImsConfig.notifyRcsAutoConfigurationRemoved();
} catch (RemoteException e) {
loge("notifyRcsAutoConfigurationRemoved failed due to " + e);
}
} else {
logd("notifyRcsAutoConfigurationRemoved failed due to IImsConfig null.");
}
}
private void clearCallbacks() {
synchronized (mRcsConfigCallbacks) {
Iterator<IRcsConfigCallback> it = mRcsConfigCallbacks.iterator();
while (it.hasNext()) {
IRcsConfigCallback cb = it.next();
if (mIImsConfig != null) {
try {
mIImsConfig.removeRcsConfigCallback(cb);
} catch (RemoteException e) {
loge("fail to removeRcsConfigCallback due to " + e);
}
}
try {
cb.onRemoved();
} catch (RemoteException e) {
logd("Failed to notify onRemoved due to dead binder of " + cb);
}
it.remove();
}
}
}
private void registerMetricsCallback() {
RcsProvisioningCallback rcsProvisioningCallback = mRcsStats.getRcsProvisioningCallback(
mSubId, mSingleRegistrationCapability == ProvisioningManager.STATUS_CAPABLE);
// if not yet registered, register callback and set registered value
if (rcsProvisioningCallback != null && !rcsProvisioningCallback.getRegistered()) {
if (addRcsConfigCallback(rcsProvisioningCallback)) {
rcsProvisioningCallback.setRegistered(true);
}
}
}
}
@VisibleForTesting
public interface FeatureConnectorFactory<U extends FeatureUpdates> {
/**
* @return a {@link FeatureConnector} associated for the given {@link FeatureUpdates}
* and slot index.
*/
FeatureConnector<U> create(Context context, int slotIndex,
FeatureConnector.Listener<U> listener, Executor executor, String logPrefix);
}
private final class RcsFeatureListener implements FeatureConnector.Listener<RcsFeatureManager> {
private final ArraySet<RcsProvisioningInfo> mRcsProvisioningInfos = new ArraySet<>();
private RcsFeatureManager mRcsFeatureManager;
private FeatureConnector<RcsFeatureManager> mConnector;
RcsFeatureListener(int slotId) {
mConnector = mFeatureFactory.create(
mPhone, slotId, this, new HandlerExecutor(mHandler), TAG);
mConnector.connect();
}
void destroy() {
mConnector.disconnect();
mConnector = null;
mRcsFeatureManager = null;
mRcsProvisioningInfos.clear();
}
void addRcsProvisioningInfo(RcsProvisioningInfo info) {
if (!mRcsProvisioningInfos.contains(info)) {
mRcsProvisioningInfos.add(info);
info.onRcsStatusChanged(mRcsFeatureManager == null ? null
: mRcsFeatureManager.getConfig());
}
}
void removeRcsProvisioningInfo(RcsProvisioningInfo info) {
mRcsProvisioningInfos.remove(info);
}
@Override
public void connectionReady(RcsFeatureManager manager) {
mRcsFeatureManager = manager;
mRcsProvisioningInfos.forEach(v -> v.onRcsStatusChanged(manager.getConfig()));
}
@Override
public void connectionUnavailable(int reason) {
mRcsFeatureManager = null;
mRcsProvisioningInfos.forEach(v -> v.onRcsStatusChanged(null));
}
}
@VisibleForTesting
public RcsProvisioningMonitor(PhoneGlobals app, Looper looper, RoleManagerAdapter roleManager,
FeatureConnectorFactory<RcsFeatureManager> factory, RcsStats rcsStats) {
mPhone = app;
mHandler = new MyHandler(looper);
mCarrierConfigManager = mPhone.getSystemService(CarrierConfigManager.class);
mSubscriptionManager = mPhone.getSystemService(SubscriptionManager.class);
mTelephonyRegistryManager = mPhone.getSystemService(TelephonyRegistryManager.class);
mRoleManager = roleManager;
mDmaPackageName = getDmaPackageName();
logv("DMA is " + mDmaPackageName);
mDmaChangedListener = new DmaChangedListener();
mFeatureFactory = factory;
mRcsStats = rcsStats;
init();
}
/**
* create an instance
*/
public static RcsProvisioningMonitor make(PhoneGlobals app) {
if (sInstance == null) {
logd("RcsProvisioningMonitor created.");
HandlerThread handlerThread = new HandlerThread(TAG);
handlerThread.start();
sInstance = new RcsProvisioningMonitor(app, handlerThread.getLooper(),
new RoleManagerAdapterImpl(app), RcsFeatureManager::getConnector,
RcsStats.getInstance());
}
return sInstance;
}
/**
* get the instance
*/
public static RcsProvisioningMonitor getInstance() {
return sInstance;
}
private void init() {
logd("init.");
IntentFilter filter = new IntentFilter();
filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
mPhone.registerReceiver(mReceiver, filter);
mTelephonyRegistryManager.addOnSubscriptionsChangedListener(
mSubChangedListener, mSubChangedListener.getHandlerExecutor());
mDmaChangedListener.register();
//initialize configs for all active sub
onSubChanged();
}
private void release() {
logd("release.");
mDmaChangedListener.unregister();
mTelephonyRegistryManager.removeOnSubscriptionsChangedListener(mSubChangedListener);
mPhone.unregisterReceiver(mReceiver);
for (int i = 0; i < mRcsFeatureListeners.size(); i++) {
mRcsFeatureListeners.valueAt(i).destroy();
}
mRcsFeatureListeners.clear();
mRcsProvisioningInfos.forEach((k, v)->v.destroy());
mRcsProvisioningInfos.clear();
mCarrierSingleRegistrationEnabledOverride.clear();
}
private void reset() {
release();
init();
}
/**
* destroy the instance
*/
@VisibleForTesting
public void destroy() {
logd("destroy it.");
release();
mHandler.getLooper().quit();
}
/**
* get the handler
*/
@VisibleForTesting
public Handler getHandler() {
return mHandler;
}
/**
* Gets the config for a subscription
*/
@VisibleForTesting
public byte[] getConfig(int subId) {
if (mRcsProvisioningInfos.containsKey(subId)) {
return mRcsProvisioningInfos.get(subId).getConfig();
}
return null;
}
/**
* Returns whether Rcs Volte single registration is enabled for the sub.
*/
public Boolean isRcsVolteSingleRegistrationEnabled(int subId) {
if (mRcsProvisioningInfos.containsKey(subId)) {
return mRcsProvisioningInfos.get(subId).getSingleRegistrationCapability()
== ProvisioningManager.STATUS_CAPABLE;
}
return null;
}
/**
* Called when the new rcs config is received
*/
public void updateConfig(int subId, byte[] config, boolean isCompressed) {
mHandler.sendMessage(mHandler.obtainMessage(
EVENT_CONFIG_RECEIVED, subId, isCompressed ? 1 : 0, config));
}
/**
* Called when the application needs rcs re-config
*/
public void requestReconfig(int subId) {
mHandler.sendMessage(mHandler.obtainMessage(EVENT_RECONFIG_REQUEST, subId, 0));
}
/**
* Called when the application registers rcs provisioning callback
*/
public boolean registerRcsProvisioningCallback(int subId, IRcsConfigCallback cb) {
RcsProvisioningInfo info = mRcsProvisioningInfos.get(subId);
// should not happen in normal case
if (info == null) {
logd("fail to register rcs provisioning callback due to subscription unavailable");
return false;
}
return info.addRcsConfigCallback(cb);
}
/**
* Called when the application unregisters rcs provisioning callback
*/
public boolean unregisterRcsProvisioningCallback(int subId, IRcsConfigCallback cb) {
RcsProvisioningInfo info = mRcsProvisioningInfos.get(subId);
// should not happen in normal case
if (info == null) {
logd("fail to unregister rcs provisioning changed due to subscription unavailable");
return false;
}
return info.removeRcsConfigCallback(cb);
}
/**
* Enables or disables test mode.
*
* <p> If test mode is enabled, any rcs config change will not update the database.
*/
public void setTestModeEnabled(boolean enabled) {
logv("setTestModeEnabled as " + enabled);
if (mTestModeEnabled != enabled) {
mTestModeEnabled = enabled;
mHandler.sendMessage(mHandler.obtainMessage(EVENT_RESET));
}
}
/**
* Returns whether the test mode is enabled.
*/
public boolean getTestModeEnabled() {
return mTestModeEnabled;
}
/**
* override the device config whether single registration is enabled
*/
public void overrideDeviceSingleRegistrationEnabled(Boolean enabled) {
mHandler.sendMessage(mHandler.obtainMessage(EVENT_DEVICE_CONFIG_OVERRIDE, enabled));
}
/**
* Overrides the carrier config whether single registration is enabled
*/
public boolean overrideCarrierSingleRegistrationEnabled(int subId, Boolean enabled) {
if (!mRcsProvisioningInfos.containsKey(subId)) {
return false;
}
mHandler.sendMessage(mHandler.obtainMessage(
EVENT_CARRIER_CONFIG_OVERRIDE, subId, 0, enabled));
return true;
}
/**
* override the rcs feature validation result for a subscription
*/
public boolean overrideImsFeatureValidation(int subId, Boolean enabled) {
if (enabled == null) {
mImsFeatureValidationOverride.remove(subId);
} else {
mImsFeatureValidationOverride.put(subId, enabled);
}
return true;
}
/**
* Returns the device config whether single registration is enabled
*/
public boolean getDeviceSingleRegistrationEnabled() {
for (RcsProvisioningInfo info : mRcsProvisioningInfos.values()) {
return (info.getSingleRegistrationCapability()
& ProvisioningManager.STATUS_DEVICE_NOT_CAPABLE) == 0;
}
return false;
}
/**
* Returns the carrier config whether single registration is enabled
*/
public boolean getCarrierSingleRegistrationEnabled(int subId) {
if (mRcsProvisioningInfos.containsKey(subId)) {
return (mRcsProvisioningInfos.get(subId).getSingleRegistrationCapability()
& ProvisioningManager.STATUS_CARRIER_NOT_CAPABLE) == 0;
}
return false;
}
/**
* Returns the rcs feature validation override value, null if it is not set.
*/
public Boolean getImsFeatureValidationOverride(int subId) {
return mImsFeatureValidationOverride.get(subId);
}
private void onDefaultMessagingApplicationChanged() {
final String packageName = getDmaPackageName();
if (!TextUtils.equals(mDmaPackageName, packageName)) {
mDmaPackageName = packageName;
logv("new default messaging application " + mDmaPackageName);
mRcsProvisioningInfos.forEach((k, v) -> {
v.notifyDma();
byte[] cachedConfig = v.getConfig();
//clear old callbacks
v.clear();
if (isAcsUsed(k)) {
logv("acs used, trigger to re-configure.");
updateConfigForSub(k, null, true);
v.triggerRcsReconfiguration();
} else {
logv("acs not used, set cached config and notify.");
v.setConfig(cachedConfig);
}
// store RCS metrics - DMA changed event
mRcsStats.onRcsClientProvisioningStats(k,
RCS_CLIENT_PROVISIONING_STATS__EVENT__DMA_CHANGED);
});
}
}
private void updateConfigForSub(int subId, byte[] config, boolean isCompressed) {
logv("updateConfigForSub, subId:" + subId + ", mTestModeEnabled:" + mTestModeEnabled);
if (!mTestModeEnabled) {
RcsConfig.updateConfigForSub(mPhone, subId, config, isCompressed);
}
}
private byte[] loadConfigForSub(int subId) {
logv("loadConfigForSub, subId:" + subId + ", mTestModeEnabled:" + mTestModeEnabled);
if (!mTestModeEnabled) {
return RcsConfig.loadRcsConfigForSub(mPhone, subId, false);
}
return null;
}
private boolean isAcsUsed(int subId) {
PersistableBundle b = mCarrierConfigManager.getConfigForSubId(subId);
if (b == null) {
return false;
}
return b.getBoolean(CarrierConfigManager.KEY_USE_ACS_FOR_RCS_BOOL);
}
private int getSingleRegistrationRequiredByCarrier(int subId) {
Boolean enabledByOverride = mCarrierSingleRegistrationEnabledOverride.get(subId);
if (enabledByOverride != null) {
return enabledByOverride ? ProvisioningManager.STATUS_CAPABLE
: ProvisioningManager.STATUS_CARRIER_NOT_CAPABLE;
}
PersistableBundle b = mCarrierConfigManager.getConfigForSubId(subId);
if (!CarrierConfigManager.isConfigForIdentifiedCarrier(b)) {
return MASK_CAP_CARRIER_INIT;
}
return b.getBoolean(CarrierConfigManager.Ims.KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL)
? ProvisioningManager.STATUS_CAPABLE
: ProvisioningManager.STATUS_CARRIER_NOT_CAPABLE;
}
private int getSingleRegistrationCapableValue(int subId) {
boolean isSingleRegistrationEnabledOnDevice =
mDeviceSingleRegistrationEnabledOverride != null
? mDeviceSingleRegistrationEnabledOverride
: mPhone.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION);
int value = (isSingleRegistrationEnabledOnDevice ? ProvisioningManager.STATUS_CAPABLE
: ProvisioningManager.STATUS_DEVICE_NOT_CAPABLE)
| getSingleRegistrationRequiredByCarrier(subId);
logv("SingleRegistrationCapableValue : " + value);
return value;
}
private void onCarrierConfigChange() {
logv("onCarrierConfigChange");
mRcsProvisioningInfos.forEach((subId, info) -> {
info.setSingleRegistrationCapability(
getSingleRegistrationCapableValue(subId));
});
}
private void onSubChanged() {
final int[] activeSubs = mSubscriptionManager.getActiveSubscriptionIdList();
final ArraySet<Integer> subsToBeDeactivated =
new ArraySet<>(mRcsProvisioningInfos.keySet());
for (int i : activeSubs) {
subsToBeDeactivated.remove(i);
if (!mRcsProvisioningInfos.containsKey(i)) {
byte[] data = loadConfigForSub(i);
int capability = getSingleRegistrationCapableValue(i);
logv("new info is created for sub : " + i + ", single registration capability :"
+ capability + ", rcs config : " + Arrays.toString(data));
mRcsProvisioningInfos.put(i, new RcsProvisioningInfo(i, capability, data));
}
}
subsToBeDeactivated.forEach(i -> {
RcsProvisioningInfo info = mRcsProvisioningInfos.remove(i);
if (info != null) {
info.destroy();
}
});
}
private void onConfigReceived(int subId, byte[] config, boolean isCompressed) {
logv("onConfigReceived, subId:" + subId + ", config:"
+ config + ", isCompressed:" + isCompressed);
RcsProvisioningInfo info = mRcsProvisioningInfos.get(subId);
if (info == null) {
logd("sub[" + subId + "] has been removed");
return;
}
info.setConfig(isCompressed ? RcsConfig.decompressGzip(config) : config);
updateConfigForSub(subId, config, isCompressed);
// Supporting ACS means config data comes from ACS
// store RCS metrics - received provisioning event
if (isAcsUsed(subId)) {
mRcsStats.onRcsAcsProvisioningStats(subId, 200,
RCS_ACS_PROVISIONING_STATS__RESPONSE_TYPE__PROVISIONING_XML,
isRcsVolteSingleRegistrationEnabled(subId));
}
}
private void onReconfigRequest(int subId) {
logv("onReconfigRequest, subId:" + subId);
RcsProvisioningInfo info = mRcsProvisioningInfos.get(subId);
if (info != null) {
info.setConfig(null);
// clear rcs config stored in db
updateConfigForSub(subId, null, true);
info.triggerRcsReconfiguration();
}
// store RCS metrics - reconfig event
mRcsStats.onRcsClientProvisioningStats(subId,
RCS_CLIENT_PROVISIONING_STATS__EVENT__TRIGGER_RCS_RECONFIGURATION);
}
private void notifyDmaForSub(int subId, int capability) {
final Intent intent = new Intent(
ProvisioningManager.ACTION_RCS_SINGLE_REGISTRATION_CAPABILITY_UPDATE);
intent.setPackage(mDmaPackageName);
intent.putExtra(ProvisioningManager.EXTRA_SUBSCRIPTION_ID, subId);
intent.putExtra(ProvisioningManager.EXTRA_STATUS, capability);
logv("notify " + intent + ", sub:" + subId + ", capability:" + capability);
// Only send permission to the default sms app if it has the correct permissions
// except test mode enabled
if (!mTestModeEnabled) {
mPhone.sendBroadcast(intent, Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION);
} else {
mPhone.sendBroadcast(intent);
}
}
private String getDmaPackageName() {
try {
return CollectionUtils.firstOrNull(mRoleManager.getRoleHolders(RoleManager.ROLE_SMS));
} catch (RuntimeException e) {
loge("Could not get dma name due to " + e);
return null;
}
}
void registerRcsFeatureListener(RcsProvisioningInfo info) {
int slotId = SubscriptionManager.getSlotIndex(info.getSubId());
RcsFeatureListener cb = mRcsFeatureListeners.get(slotId);
if (cb == null) {
cb = new RcsFeatureListener(slotId);
mRcsFeatureListeners.put(slotId, cb);
}
cb.addRcsProvisioningInfo(info);
}
void unregisterRcsFeatureListener(RcsProvisioningInfo info) {
// make sure the info to be removed in any case, even the slotId changed or invalid.
for (int i = 0; i < mRcsFeatureListeners.size(); i++) {
mRcsFeatureListeners.valueAt(i).removeRcsProvisioningInfo(info);
}
}
private static boolean booleanEquals(Boolean val1, Boolean val2) {
return (val1 == null && val2 == null)
|| (Boolean.TRUE.equals(val1) && Boolean.TRUE.equals(val2))
|| (Boolean.FALSE.equals(val1) && Boolean.FALSE.equals(val2));
}
private static void logv(String msg) {
if (DBG) {
Rlog.d(TAG, msg);
}
}
private static void logi(String msg) {
Rlog.i(TAG, msg);
}
private static void logd(String msg) {
Rlog.d(TAG, msg);
}
private static void loge(String msg) {
Rlog.e(TAG, msg);
}
/**
* {@link RoleManager} is final so we have to wrap the implementation for testing.
*/
@VisibleForTesting
public interface RoleManagerAdapter {
/** See {@link RoleManager#getRoleHolders(String)} */
List<String> getRoleHolders(String roleName);
/** See {@link RoleManager#addOnRoleHoldersChangedListenerAsUser} */
void addOnRoleHoldersChangedListenerAsUser(Executor executor,
OnRoleHoldersChangedListener listener, UserHandle user);
/** See {@link RoleManager#removeOnRoleHoldersChangedListenerAsUser} */
void removeOnRoleHoldersChangedListenerAsUser(OnRoleHoldersChangedListener listener,
UserHandle user);
}
private static class RoleManagerAdapterImpl implements RoleManagerAdapter {
private final RoleManager mRoleManager;
private RoleManagerAdapterImpl(Context context) {
mRoleManager = context.getSystemService(RoleManager.class);
}
@Override
public List<String> getRoleHolders(String roleName) {
return mRoleManager.getRoleHolders(roleName);
}
@Override
public void addOnRoleHoldersChangedListenerAsUser(Executor executor,
OnRoleHoldersChangedListener listener, UserHandle user) {
mRoleManager.addOnRoleHoldersChangedListenerAsUser(executor, listener, user);
}
@Override
public void removeOnRoleHoldersChangedListenerAsUser(OnRoleHoldersChangedListener listener,
UserHandle user) {
mRoleManager.removeOnRoleHoldersChangedListenerAsUser(listener, user);
}
}
}